+ def cat_batch(self, refs):
+ self._require_command(b'cat-batch')
+ self.check_busy()
+ self._busy = b'cat-batch'
+ conn = self.conn
+ conn.write(b'cat-batch\n')
+ # FIXME: do we want (only) binary protocol?
+ for ref in refs:
+ assert ref
+ assert b'\n' not in ref
+ conn.write(ref)
+ conn.write(b'\n')
+ conn.write(b'\n')
+ for ref in refs:
+ info = conn.readline()
+ if info == b'missing\n':
+ yield None, None, None, None
+ continue
+ if not (info and info.endswith(b'\n')):
+ raise ClientError('Hit EOF while looking for object info: %r'
+ % info)
+ oidx, oid_t, size = info.split(b' ')
+ size = int(size)
+ cr = chunkyreader(conn, size)
+ yield oidx, oid_t, size, cr
+ detritus = next(cr, None)
+ if detritus:
+ raise ClientError('unexpected leftover data ' + repr(detritus))
+ # FIXME: confusing
+ not_ok = self.check_ok()
+ if not_ok:
+ raise not_ok
+ self._not_busy()
+
+ def refs(self, patterns=None, limit_to_heads=False, limit_to_tags=False):
+ patterns = patterns or tuple()
+ self._require_command(b'refs')
+ self.check_busy()
+ self._busy = b'refs'
+ conn = self.conn
+ conn.write(b'refs %d %d\n' % (1 if limit_to_heads else 0,
+ 1 if limit_to_tags else 0))
+ for pattern in patterns:
+ assert b'\n' not in pattern
+ conn.write(pattern)
+ conn.write(b'\n')
+ conn.write(b'\n')
+ for line in lines_until_sentinel(conn, b'\n', ClientError):
+ line = line[:-1]
+ oidx, name = line.split(b' ')
+ if len(oidx) != 40:
+ raise ClientError('Invalid object fingerprint in %r' % line)
+ if not name:
+ raise ClientError('Invalid reference name in %r' % line)
+ yield name, unhexlify(oidx)
+ # FIXME: confusing
+ not_ok = self.check_ok()
+ if not_ok:
+ raise not_ok
+ self._not_busy()
+
+ def rev_list(self, refs, parse=None, format=None):
+ """See git.rev_list for the general semantics, but note that with the
+ current interface, the parse function must be able to handle
+ (consume) any blank lines produced by the format because the
+ first one received that it doesn't consume will be interpreted
+ as a terminator for the entire rev-list result.
+
+ """
+ self._require_command(b'rev-list')
+ if format:
+ assert b'\n' not in format
+ assert parse
+ for ref in refs:
+ assert ref
+ assert b'\n' not in ref
+ self.check_busy()
+ self._busy = b'rev-list'
+ conn = self.conn
+ conn.write(b'rev-list\n')
+ conn.write(b'\n')
+ if format:
+ conn.write(format)
+ conn.write(b'\n')
+ for ref in refs:
+ conn.write(ref)
+ conn.write(b'\n')
+ conn.write(b'\n')
+ if not format:
+ for line in lines_until_sentinel(conn, b'\n', ClientError):
+ line = line.strip()
+ assert len(line) == 40
+ yield line
+ else:
+ for line in lines_until_sentinel(conn, b'\n', ClientError):
+ if not line.startswith(b'commit '):
+ raise ClientError('unexpected line ' + repr(line))
+ cmt_oidx = line[7:].strip()
+ assert len(cmt_oidx) == 40
+ yield cmt_oidx, parse(conn)
+ # FIXME: confusing
+ not_ok = self.check_ok()
+ if not_ok:
+ raise not_ok
+ self._not_busy()
+
+ def resolve(self, path, parent=None, want_meta=True, follow=False):
+ self._require_command(b'resolve')
+ self.check_busy()
+ self._busy = b'resolve'
+ conn = self.conn
+ conn.write(b'resolve %d\n' % ((1 if want_meta else 0)
+ | (2 if follow else 0)
+ | (4 if parent else 0)))
+ if parent:
+ vfs.write_resolution(conn, parent)
+ write_bvec(conn, path)
+ success = ord(conn.read(1))
+ assert success in (0, 1)
+ if success:
+ result = vfs.read_resolution(conn)
+ else:
+ result = vfs.read_ioerror(conn)
+ # FIXME: confusing
+ not_ok = self.check_ok()
+ if not_ok:
+ raise not_ok
+ self._not_busy()
+ if isinstance(result, vfs.IOError):
+ raise result
+ return result
+