File: tag.py

package info (click to toggle)
python-git 3.1.45-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,772 kB
  • sloc: python: 18,642; sh: 186; makefile: 78
file content (140 lines) | stat: -rw-r--r-- 4,441 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
#
# This module is part of GitPython and is released under the
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/

"""Provides an :class:`~git.objects.base.Object`-based type for annotated tags.

This defines the :class:`TagObject` class, which represents annotated tags.
For lightweight tags, see the :mod:`git.refs.tag` module.
"""

__all__ = ["TagObject"]

import sys

from git.compat import defenc
from git.util import Actor, hex_to_bin

from . import base
from .util import get_object_type_by_name, parse_actor_and_date

# typing ----------------------------------------------

from typing import List, TYPE_CHECKING, Union

if sys.version_info >= (3, 8):
    from typing import Literal
else:
    from typing_extensions import Literal

if TYPE_CHECKING:
    from git.repo import Repo

    from .blob import Blob
    from .commit import Commit
    from .tree import Tree

# ---------------------------------------------------


class TagObject(base.Object):
    """Annotated (i.e. non-lightweight) tag carrying additional information about an
    object we are pointing to.

    See :manpage:`gitglossary(7)` on "tag object":
    https://git-scm.com/docs/gitglossary#def_tag_object
    """

    type: Literal["tag"] = "tag"

    __slots__ = (
        "object",
        "tag",
        "tagger",
        "tagged_date",
        "tagger_tz_offset",
        "message",
    )

    def __init__(
        self,
        repo: "Repo",
        binsha: bytes,
        object: Union[None, base.Object] = None,
        tag: Union[None, str] = None,
        tagger: Union[None, Actor] = None,
        tagged_date: Union[int, None] = None,
        tagger_tz_offset: Union[int, None] = None,
        message: Union[str, None] = None,
    ) -> None:  # @ReservedAssignment
        """Initialize a tag object with additional data.

        :param repo:
            Repository this object is located in.

        :param binsha:
            20 byte SHA1.

        :param object:
            :class:`~git.objects.base.Object` instance of object we are pointing to.

        :param tag:
            Name of this tag.

        :param tagger:
            :class:`~git.util.Actor` identifying the tagger.

        :param tagged_date: int_seconds_since_epoch
            The DateTime of the tag creation.
            Use :func:`time.gmtime` to convert it into a different format.

        :param tagger_tz_offset: int_seconds_west_of_utc
            The timezone that the `tagged_date` is in, in a format similar to
            :attr:`time.altzone`.
        """
        super().__init__(repo, binsha)
        if object is not None:
            self.object: Union["Commit", "Blob", "Tree", "TagObject"] = object
        if tag is not None:
            self.tag = tag
        if tagger is not None:
            self.tagger = tagger
        if tagged_date is not None:
            self.tagged_date = tagged_date
        if tagger_tz_offset is not None:
            self.tagger_tz_offset = tagger_tz_offset
        if message is not None:
            self.message = message

    def _set_cache_(self, attr: str) -> None:
        """Cache all our attributes at once."""
        if attr in TagObject.__slots__:
            ostream = self.repo.odb.stream(self.binsha)
            lines: List[str] = ostream.read().decode(defenc, "replace").splitlines()

            _obj, hexsha = lines[0].split(" ")
            _type_token, type_name = lines[1].split(" ")
            object_type = get_object_type_by_name(type_name.encode("ascii"))
            self.object = object_type(self.repo, hex_to_bin(hexsha))

            self.tag = lines[2][4:]  # tag <tag name>

            if len(lines) > 3:
                tagger_info = lines[3]  # tagger <actor> <date>
                (
                    self.tagger,
                    self.tagged_date,
                    self.tagger_tz_offset,
                ) = parse_actor_and_date(tagger_info)

            # Line 4 empty - it could mark the beginning of the next header.
            # In case there really is no message, it would not exist.
            # Otherwise a newline separates header from message.
            if len(lines) > 5:
                self.message = "\n".join(lines[5:])
            else:
                self.message = ""
        # END check our attributes
        else:
            super()._set_cache_(attr)