def _encode_packobj(type, content, compression_level=1):
+ if compression_level not in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9):
+ raise ValueError('invalid compression level %s' % compression_level)
szout = ''
sz = len(content)
szbits = (sz & 0x0f) | (_typemap[type]<<4)
break
szbits = sz & 0x7f
sz >>= 7
- if compression_level > 9:
- compression_level = 9
- elif compression_level < 0:
- compression_level = 0
z = zlib.compressobj(compression_level)
yield szout
yield z.compress(content)
self.abort()
+class MissingObject(KeyError):
+ def __init__(self, id):
+ self.id = id
+ KeyError.__init__(self, 'object %r is missing' % id.encode('hex'))
+
+
_ver_warned = 0
class CatPipe:
"""Link to 'git cat-file' that is used to retrieve blob data."""
hdr = self.p.stdout.readline()
if hdr.endswith(' missing\n'):
self.inprogress = None
- raise KeyError('blob %r is missing' % id)
+ raise MissingObject(id.decode('hex'))
spl = hdr.split(' ')
if len(spl) != 3 or len(spl[0]) != 40:
raise GitError('expected blob, got %r' % spl)
# ...
-def _walk_object(cat_pipe, id,
- parent_path, chunk_path,
- mode=None,
- stop_at=None,
- include_data=None):
-
- if stop_at and stop_at(id):
- return
-
- item_it = cat_pipe.get(id) # FIXME: use include_data
- type = item_it.next()
-
- if type not in ('blob', 'commit', 'tree'):
- raise Exception('unexpected repository object type %r' % type)
-
- # FIXME: set the mode based on the type when the mode is None
-
- if type == 'blob' and not include_data:
- # Dump data until we can ask cat_pipe not to fetch it
- for ignored in item_it:
- pass
- data = None
- else:
- data = ''.join(item_it)
-
- yield WalkItem(id=id, type=type,
- chunk_path=chunk_path, path=parent_path,
- mode=mode,
- data=(data if include_data else None))
-
- if type == 'commit':
- commit_items = parse_commit(data)
- tree_id = commit_items.tree
- for x in _walk_object(cat_pipe, tree_id, parent_path, chunk_path,
- mode=hashsplit.GIT_MODE_TREE,
- stop_at=stop_at,
- include_data=include_data):
- yield x
- parents = commit_items.parents
- for pid in parents:
- for x in _walk_object(cat_pipe, pid, parent_path, chunk_path,
- mode=mode, # Same mode as this child
- stop_at=stop_at,
- include_data=include_data):
- yield x
- elif type == 'tree':
- for mode, name, ent_id in tree_decode(data):
- demangled, bup_type = demangle_name(name, mode)
- if chunk_path:
- sub_path = parent_path
- sub_chunk_path = chunk_path + [name]
- else:
- sub_path = parent_path + [name]
- if bup_type == BUP_CHUNKED:
- sub_chunk_path = ['']
- else:
- sub_chunk_path = chunk_path
- for x in _walk_object(cat_pipe, ent_id.encode('hex'),
- sub_path, sub_chunk_path,
- mode=mode,
- stop_at=stop_at,
- include_data=include_data):
- yield x
-
-
def walk_object(cat_pipe, id,
stop_at=None,
include_data=None):
"""Yield everything reachable from id via cat_pipe as a WalkItem,
- stopping whenever stop_at(id) returns true."""
- return _walk_object(cat_pipe, id, [], [],
- stop_at=stop_at,
- include_data=include_data)
+ stopping whenever stop_at(id) returns true. Throw MissingObject
+ if a hash encountered is missing from the repository.
+
+ """
+ # Maintain the pending stack on the heap to avoid stack overflow
+ pending = [(id, [], [], None)]
+ while len(pending):
+ id, parent_path, chunk_path, mode = pending.pop()
+ if stop_at and stop_at(id):
+ continue
+
+ item_it = cat_pipe.get(id) # FIXME: use include_data
+ type = item_it.next()
+ if type not in ('blob', 'commit', 'tree'):
+ raise Exception('unexpected repository object type %r' % type)
+
+ # FIXME: set the mode based on the type when the mode is None
+ if type == 'blob' and not include_data:
+ # Dump data until we can ask cat_pipe not to fetch it
+ for ignored in item_it:
+ pass
+ data = None
+ else:
+ data = ''.join(item_it)
+
+ yield WalkItem(id=id, type=type,
+ chunk_path=chunk_path, path=parent_path,
+ mode=mode,
+ data=(data if include_data else None))
+
+ if type == 'commit':
+ commit_items = parse_commit(data)
+ for pid in commit_items.parents:
+ pending.append((pid, parent_path, chunk_path, mode))
+ pending.append((commit_items.tree, parent_path, chunk_path,
+ hashsplit.GIT_MODE_TREE))
+ elif type == 'tree':
+ for mode, name, ent_id in tree_decode(data):
+ demangled, bup_type = demangle_name(name, mode)
+ if chunk_path:
+ sub_path = parent_path
+ sub_chunk_path = chunk_path + [name]
+ else:
+ sub_path = parent_path + [name]
+ if bup_type == BUP_CHUNKED:
+ sub_chunk_path = ['']
+ else:
+ sub_chunk_path = chunk_path
+ pending.append((ent_id.encode('hex'), sub_path, sub_chunk_path,
+ mode))