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

        We can handle a line as follows::

            %c %-*s %-*s -> %s%s

        Where ``c`` is either a space, ``!``, ``+``, ``-``, ``*``, 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("Failed to parse line: %r" % line)

        # Parse lines.
        remote_local_ref_str: str
        (
            control_character,
            operation,
            local_remote_ref,
            remote_local_ref_str,
            note,
        ) = match.groups()
        control_character = cast(flagKeyLiteral, control_character)
        try:
            _new_hex_sha, _fetch_operation, fetch_note = fetch_line.split("\t")
            ref_type_name, fetch_note = fetch_note.split(" ", 1)
        except ValueError as e:  # unpack error
            raise ValueError("Failed to parse FETCH_HEAD line: %r" % fetch_line) from e

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

        # Parse operation string for more info.
        # This makes no sense for symbolic refs, but we parse it anyway.
        old_commit: Union[AnyGitObject, None] = 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: Optional[Type[SymbolicReference]] = None
        if remote_local_ref_str == "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 it's
            # enough to know what to do, and it's new in git 1.7something.
            ref_type = RemoteReference
        elif "/" in ref_type_name:
            # If the fetch spec look something like '+refs/pull/*:refs/heads/pull/*',
            # and is thus pretty much anything the user wants, we will have trouble
            # determining 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: Optional[PathLike] = None
            remote_local_ref_str = remote_local_ref_str.strip()

            if remote_local_ref_str.startswith(Reference._common_path_default + "/"):
                # Always use actual type if we get absolute paths. This will always be
                # the case if something is fetched outside of refs/remotes (if its not a
                # tag).
                ref_path = remote_local_ref_str
                if ref_type is not TagReference and not remote_local_ref_str.startswith(
                    RemoteReference._common_path_default + "/"
                ):
                    ref_type = Reference
                # END downgrade remote reference
            elif ref_type is TagReference and "tags/" in remote_local_ref_str:
                # Even though it's a tag, it is located in refs/remotes.
                ref_path = join_path(RemoteReference._common_path_default, remote_local_ref_str)
            else:
                ref_path = join_path(ref_type._common_path_default, remote_local_ref_str)
            # 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)
Parse information from the given line as returned by ``git-fetch -v`` and
        return a new :class:`FetchInfo` object representing this information.

        We can handle a line as follows::

            %c %-*s %-*s -> %s%s

        Where ``c`` is either a space, ``!``, ``+``, ``-``, ``*``, 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
        
git/remote.py