Example #1
0
    def _pack_info(self, sha):
        """:return: tuple(entity, index) for an item at the given sha
        :param sha: 20 or 40 byte sha
        :raise BadObject:
        **Note:** This method is not thread-safe, but may be hit in multi-threaded
            operation. The worst thing that can happen though is a counter that
            was not incremented, or the list being in wrong order. So we safe
            the time for locking here, lets see how that goes"""
        # presort ?
        if self._hit_count % self._sort_interval == 0:
            self._sort_entities()
        # END update sorting

        for item in self._entities:
            index = item[2](sha)
            if index is not None:
                item[0] += 1  # one hit for you
                self._hit_count += 1  # general hit count
                return (item[1], index)
            # END index found in pack
        # END for each item

        # no hit, see whether we have to update packs
        # NOTE: considering packs don't change very often, we safe this call
        # and leave it to the super-caller to trigger that
        raise BadObject(sha)
Example #2
0
def name_to_object(repo, name):
    """:return: object specified by the given name, hexshas ( short and long )
		as well as references are supported"""
    hexsha = None

    # is it a hexsha ? Try the most common ones, which is 7 to 40
    if repo.re_hexsha_shortened.match(name):
        if len(name) != 40:
            # find long sha for short sha
            hexsha = short_to_long(repo.odb, name)
        else:
            hexsha = name
        # END handle short shas
    else:
        for base in ('%s', 'refs/%s', 'refs/tags/%s', 'refs/heads/%s',
                     'refs/remotes/%s', 'refs/remotes/%s/HEAD'):
            try:
                hexsha = SymbolicReference.dereference_recursive(
                    repo, base % name)
                break
            except ValueError:
                pass
        # END for each base
    # END handle hexsha

    # tried everything ? fail
    if hexsha is None:
        raise BadObject(name)
    # END assert hexsha was found

    return Object.new_from_sha(repo, hex_to_bin(hexsha))
Example #3
0
    def partial_to_complete_sha(self, partial_binsha, canonical_length):
        """:return: 20 byte sha as inferred by the given partial binary sha
        :param partial_binsha: binary sha with less than 20 bytes
        :param canonical_length: length of the corresponding canonical representation.
            It is required as binary sha's cannot display whether the original hex sha
            had an odd or even number of characters
        :raise AmbiguousObjectName:
        :raise BadObject: """
        candidate = None
        for item in self._entities:
            item_index = item[1].index().partial_sha_to_index(
                partial_binsha, canonical_length)
            if item_index is not None:
                sha = item[1].index().sha(item_index)
                if candidate and candidate != sha:
                    raise AmbiguousObjectName(partial_binsha)
                candidate = sha
            # END handle full sha could be found
        # END for each entity

        if candidate:
            return candidate

        # still not found ?
        raise BadObject(partial_binsha)
Example #4
0
def name_to_object(repo, name, return_ref=False):
    """
	:return: object specified by the given name, hexshas ( short and long )
		as well as references are supported
	:param return_ref: if name specifies a reference, we will return the reference
		instead of the object. Otherwise it will raise BadObject
	"""
    hexsha = None

    # is it a hexsha ? Try the most common ones, which is 7 to 40
    if repo.re_hexsha_shortened.match(name):
        if len(name) != 40:
            # find long sha for short sha
            hexsha = short_to_long(repo.odb, name)
        else:
            hexsha = name
        # END handle short shas
    #END find sha if it matches

    # if we couldn't find an object for what seemed to be a short hexsha
    # try to find it as reference anyway, it could be named 'aaa' for instance
    if hexsha is None:
        for base in ('%s', 'refs/%s', 'refs/tags/%s', 'refs/heads/%s',
                     'refs/remotes/%s', 'refs/remotes/%s/HEAD'):
            try:
                hexsha = SymbolicReference.dereference_recursive(
                    repo, base % name)
                if return_ref:
                    return SymbolicReference(repo, base % name)
                #END handle symbolic ref
                break
            except ValueError:
                pass
        # END for each base
    # END handle hexsha

    # didn't find any ref, this is an error
    if return_ref:
        raise BadObject("Couldn't find reference named %r" % name)
    #END handle return ref

    # tried everything ? fail
    if hexsha is None:
        raise BadObject(name)
    # END assert hexsha was found

    return Object.new_from_sha(repo, hex_to_bin(hexsha))
Example #5
0
 def stream(self, sha):
     try:
         ostream = self._cache[sha]
         # rewind stream for the next one to read
         ostream.stream.seek(0)
         return ostream
     except KeyError:
         raise BadObject(sha)
Example #6
0
 def _map_loose_object(self, sha):
     """
     :return: memory map of that file to allow random read access
     :raise BadObject: if object could not be located"""
     db_path = self.db_path(self.object_path(bin_to_hex(sha)))
     try:
         return file_contents_ro_filepath(db_path, flags=self._fd_open_flags)
     except OSError as e:
         if e.errno != ENOENT:
             # try again without noatime
             try:
                 return file_contents_ro_filepath(db_path)
             except OSError:
                 raise BadObject(sha)
             # didn't work because of our flag, don't try it again
             self._fd_open_flags = 0
         else:
             raise BadObject(sha)
Example #7
0
 def partial_to_complete_sha_hex(self, partial_hexsha: str) -> bytes:
     """:return: Full binary 20 byte sha from the given partial hexsha
     :raise AmbiguousObjectName:
     :raise BadObject:
     :note: currently we only raise BadObject as git does not communicate
         AmbiguousObjects separately"""
     try:
         hexsha, _typename, _size = self._git.get_object_header(partial_hexsha)
         return hex_to_bin(hexsha)
     except (GitCommandError, ValueError) as e:
         raise BadObject(partial_hexsha) from e
    def _object(self, sha, as_stream, index=-1):
        """:return: OInfo or OStream object providing information about the given sha
		:param index: if not -1, its assumed to be the sha's index in the IndexFile"""
        # its a little bit redundant here, but it needs to be efficient
        if index < 0:
            index = self._sha_to_index(sha)
        if sha is None:
            sha = self._index.sha(index)
        # END assure sha is present ( in output )
        offset = self._index.offset(index)
        type_id, uncomp_size, data_rela_offset = pack_object_header_info(
            buffer(self._pack._data, offset))
        if as_stream:
            if type_id not in delta_types:
                packstream = self._pack.stream(offset)
                return OStream(sha, packstream.type, packstream.size,
                               packstream.stream)
            # END handle non-deltas

            # produce a delta stream containing all info
            # To prevent it from applying the deltas when querying the size,
            # we extract it from the delta stream ourselves
            streams = self.collect_streams_at_offset(offset)
            buf = streams[0].read(512)
            offset, src_size = msb_size(buf)
            offset, target_size = msb_size(buf, offset)

            streams[0].stream.seek(
                0)  # assure it can be read by the delta reader
            dstream = DeltaApplyReader.new(streams)

            return OStream(sha, dstream.type, target_size, dstream)
        else:
            if type_id not in delta_types:
                return OInfo(sha, type_id_to_type_map[type_id], uncomp_size)
            # END handle non-deltas

            # deltas are a little tougher - unpack the first bytes to obtain
            # the actual target size, as opposed to the size of the delta data
            streams = self.collect_streams_at_offset(offset)
            buf = streams[0].read(512)
            offset, src_size = msb_size(buf)
            offset, target_size = msb_size(buf, offset)

            # collect the streams to obtain the actual object type
            if streams[-1].type_id in delta_types:
                raise BadObject(sha, "Could not resolve delta object")
            return OInfo(sha, streams[-1].type, target_size)
Example #9
0
 def partial_to_complete_sha_hex(self, partial_hexsha):
     """:return: 20 byte binary sha1 string which matches the given name uniquely
     :param name: hexadecimal partial name
     :raise AmbiguousObjectName: 
     :raise BadObject: """
     candidate = None
     for binsha in self.sha_iter():
         if bin_to_hex(binsha).startswith(partial_hexsha):
             # it can't ever find the same object twice
             if candidate is not None:
                 raise AmbiguousObjectName(partial_hexsha)
             candidate = binsha
     # END for each object
     if candidate is None:
         raise BadObject(partial_hexsha)
     return candidate
Example #10
0
    def readable_db_object_path(self, hexsha):
        """
        :return: readable object path to the object identified by hexsha
        :raise BadObject: If the object file does not exist"""
        try:
            return self._hexsha_to_file[hexsha]
        except KeyError:
            pass
        # END ignore cache misses

        # try filesystem
        path = self.db_path(self.object_path(hexsha))
        if exists(path):
            self._hexsha_to_file[hexsha] = path
            return path
        # END handle cache
        raise BadObject(hexsha)
Example #11
0
    def _db_query(self, sha):
        """:return: database containing the given 20 byte sha
		:raise BadObject:"""
        # most databases use binary representations, prevent converting
        # it everytime a database is being queried
        try:
            return self._db_cache[sha]
        except KeyError:
            pass
        # END first level cache

        for db in self._dbs:
            if db.has_object(sha):
                self._db_cache[sha] = db
                return db
        # END for each database
        raise BadObject(sha)
Example #12
0
    def partial_to_complete_sha_hex(self, partial_hexsha):
        """
		:return: 20 byte binary sha1 from the given less-than-40 byte hexsha
		:param partial_hexsha: hexsha with less than 40 byte
		:raise AmbiguousObjectName: """
        databases = list()
        _databases_recursive(self, databases)

        len_partial_hexsha = len(partial_hexsha)
        if len_partial_hexsha % 2 != 0:
            partial_binsha = hex_to_bin(partial_hexsha + "0")
        else:
            partial_binsha = hex_to_bin(partial_hexsha)
        # END assure successful binary conversion

        candidate = None
        for db in databases:
            full_bin_sha = None
            try:
                if hasattr(db, 'partial_to_complete_sha_hex'):
                    full_bin_sha = db.partial_to_complete_sha_hex(
                        partial_hexsha)
                else:
                    full_bin_sha = db.partial_to_complete_sha(
                        partial_binsha, len_partial_hexsha)
                # END handle database type
            except BadObject:
                continue
            # END ignore bad objects
            if full_bin_sha:
                if candidate and candidate != full_bin_sha:
                    raise AmbiguousObjectName(partial_hexsha)
                candidate = full_bin_sha
            # END handle candidate
        # END for each db
        if not candidate:
            raise BadObject(partial_binsha)
        return candidate
Example #13
0
def rev_parse(repo, rev):
    """
	:return: Object at the given revision, either Commit, Tag, Tree or Blob
	:param rev: git-rev-parse compatible revision specification, please see
		http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html
		for details
	:note: Currently there is no access to the rev-log, rev-specs may only contain
		topological tokens such ~ and ^.
	:raise BadObject: if the given revision could not be found"""
    if '@' in rev:
        raise ValueError("There is no rev-log support yet")

    # colon search mode ?
    if rev.startswith(':/'):
        # colon search mode
        raise NotImplementedError("commit by message search ( regex )")
    # END handle search

    obj = None
    output_type = "commit"
    start = 0
    parsed_to = 0
    lr = len(rev)
    while start < lr:
        if rev[start] not in "^~:":
            start += 1
            continue
        # END handle start

        if obj is None:
            # token is a rev name
            obj = name_to_object(repo, rev[:start])
        # END initialize obj on first token

        token = rev[start]
        start += 1

        # try to parse {type}
        if start < lr and rev[start] == '{':
            end = rev.find('}', start)
            if end == -1:
                raise ValueError("Missing closing brace to define type in %s" %
                                 rev)
            output_type = rev[start + 1:end]  # exclude brace

            # handle type
            if output_type == 'commit':
                pass  # default
            elif output_type == 'tree':
                try:
                    obj = to_commit(obj).tree
                except (AttributeError, ValueError):
                    pass  # error raised later
                # END exception handling
            elif output_type in ('', 'blob'):
                if obj.type == 'tag':
                    obj = deref_tag(obj)
                else:
                    # cannot do anything for non-tags
                    pass
                # END handle tag
            else:
                raise ValueError("Invalid output type: %s ( in %s )" %
                                 (output_type, rev))
            # END handle output type

            # empty output types don't require any specific type, its just about dereferencing tags
            if output_type and obj.type != output_type:
                raise ValueError(
                    "Could not accomodate requested object type %r, got %s" %
                    (output_type, obj.type))
            # END verify ouput type

            start = end + 1  # skip brace
            parsed_to = start
            continue
        # END parse type

        # try to parse a number
        num = 0
        if token != ":":
            found_digit = False
            while start < lr:
                if rev[start] in digits:
                    num = num * 10 + int(rev[start])
                    start += 1
                    found_digit = True
                else:
                    break
                # END handle number
            # END number parse loop

            # no explicit number given, 1 is the default
            # It could be 0 though
            if not found_digit:
                num = 1
            # END set default num
        # END number parsing only if non-blob mode

        parsed_to = start
        # handle hiererarchy walk
        try:
            if token == "~":
                obj = to_commit(obj)
                for item in xrange(num):
                    obj = obj.parents[0]
                # END for each history item to walk
            elif token == "^":
                obj = to_commit(obj)
                # must be n'th parent
                if num:
                    obj = obj.parents[num - 1]
            elif token == ":":
                if obj.type != "tree":
                    obj = obj.tree
                # END get tree type
                obj = obj[rev[start:]]
                parsed_to = lr
            else:
                raise ValueError("Invalid token: %r" % token)
            # END end handle tag
        except (IndexError, AttributeError):
            raise BadObject("Invalid Revision in %s" % rev)
        # END exception handling
    # END parse loop

    # still no obj ? Its probably a simple name
    if obj is None:
        obj = name_to_object(repo, rev)
        parsed_to = lr
    # END handle simple name

    if obj is None:
        raise ValueError("Revision specifier could not be parsed: %s" % rev)

    if parsed_to != lr:
        raise ValueError(
            "Didn't consume complete rev spec %s, consumed part: %s" %
            (rev, rev[:parsed_to]))

    return obj
Example #14
0
 def _sha_to_index(self, sha):
     """:return: index for the given sha, or raise"""
     index = self._index.sha_to_index(sha)
     if index is None:
         raise BadObject(sha)
     return index