@classmethod
    def _from_line(cls, repo, line, fetch_line):
        """Parse information from the given line as returned by git-fetch -v
        and return a new FetchInfo object representing this information.

        We can handle a line as follows
        "%c %-*s %-*s -> %s%s"

        Where c is either ' ', !, +, -, *, or =
        ! means error
        + means success forcing update
        - means a tag was updated
        * means birth of new branch or tag
        = means the head was up to date ( and not moved )
        ' ' means a fast-forward

        fetch line is the corresponding line from FETCH_HEAD, like
        acb0fa8b94ef421ad60c8507b634759a472cd56c    not-for-merge   branch '0.1.7RC' of /tmp/tmpya0vairemote_repo"""
        match = cls.re_fetch_result.match(line)
        if match is None:
            raise ValueError(line % "Failed to parse line: %r")

        # parse lines
        control_character, operation, local_remote_ref, remote_local_ref, note = match.groups()
        try:
            new_hex_sha, fetch_operation, fetch_note = fetch_line.split("\t")
            ref_type_name, fetch_note = fetch_note.split(' ', 1)
        except ValueError:  # unpack error
            raise ValueError("Failed to parse FETCH_HEAD line: %r" % fetch_line)

        # parse flags from control_character
        flags = 0
        try:
            flags |= cls._flag_map[control_character]
        except KeyError:
            raise ValueError("Control character %r unknown as parsed from line %r" % (control_character, line))
        # END control char exception hanlding

        # parse operation string for more info - makes no sense for symbolic refs, but we parse it anyway
        old_commit = None
        is_tag_operation = False
        if 'rejected' in operation:
            flags |= cls.REJECTED
        if 'new tag' in operation:
            flags |= cls.NEW_TAG
            is_tag_operation = True
        if 'tag update' in operation:
            flags |= cls.TAG_UPDATE
            is_tag_operation = True
        if 'new branch' in operation:
            flags |= cls.NEW_HEAD
        if '...' in operation or '..' in operation:
            split_token = '...'
            if control_character == ' ':
                split_token = split_token[:-1]
            old_commit = repo.rev_parse(operation.split(split_token)[0])
        # END handle refspec

        # handle FETCH_HEAD and figure out ref type
        # If we do not specify a target branch like master:refs/remotes/origin/master,
        # the fetch result is stored in FETCH_HEAD which destroys the rule we usually
        # have. In that case we use a symbolic reference which is detached
        ref_type = None
        if remote_local_ref == "FETCH_HEAD":
            ref_type = SymbolicReference
        elif ref_type_name == "tag" or is_tag_operation:
            # the ref_type_name can be branch, whereas we are still seeing a tag operation. It happens during
            # testing, which is based on actual git operations
            ref_type = TagReference
        elif ref_type_name in ("remote-tracking", "branch"):
            # note: remote-tracking is just the first part of the 'remote-tracking branch' token.
            # We don't parse it correctly, but its enough to know what to do, and its new in git 1.7something
            ref_type = RemoteReference
        elif '/' in ref_type_name:
            # If the fetch spec look something like this '+refs/pull/*:refs/heads/pull/*', and is thus pretty
            # much anything the user wants, we will have trouble to determine what's going on
            # For now, we assume the local ref is a Head
            ref_type = Head
        else:
            raise TypeError("Cannot handle reference type: %r" % ref_type_name)
        # END handle ref type

        # create ref instance
        if ref_type is SymbolicReference:
            remote_local_ref = ref_type(repo, "FETCH_HEAD")
        else:
            # determine prefix. Tags are usually pulled into refs/tags, they may have subdirectories.
            # It is not clear sometimes where exactly the item is, unless we have an absolute path as indicated
            # by the 'ref/' prefix. Otherwise even a tag could be in refs/remotes, which is when it will have the
            # 'tags/' subdirectory in its path.
            # We don't want to test for actual existence, but try to figure everything out analytically.
            ref_path = None
            remote_local_ref = remote_local_ref.strip()
            if remote_local_ref.startswith(Reference._common_path_default + "/"):
                # always use actual type if we get absolute paths
                # Will always be the case if something is fetched outside of refs/remotes (if its not a tag)
                ref_path = remote_local_ref
                if ref_type is not TagReference and not \
                   remote_local_ref.startswith(RemoteReference._common_path_default + "/"):
                    ref_type = Reference
                # END downgrade remote reference
            elif ref_type is TagReference and 'tags/' in remote_local_ref:
                # even though its a tag, it is located in refs/remotes
                ref_path = join_path(RemoteReference._common_path_default, remote_local_ref)
            else:
                ref_path = join_path(ref_type._common_path_default, remote_local_ref)
            # END obtain refpath

            # even though the path could be within the git conventions, we make
            # sure we respect whatever the user wanted, and disabled path checking
            remote_local_ref = ref_type(repo, ref_path, check_path=False)
        # END create ref instance

        note = (note and note.strip()) or ''

        return cls(remote_local_ref, flags, note, old_commit, local_remote_ref)
dataset/ETHPy150Open gitpython-developers/GitPython/git/remote.py/FetchInfo._from_line/swapped operands of `%`