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
|
from symbolic import SymbolicReference
import os
from git.objects import Object
from git.util import (
LazyMixin,
Iterable,
)
from gitdb.util import (
isfile,
hex_to_bin
)
__all__ = ["Reference"]
class Reference(SymbolicReference, LazyMixin, Iterable):
"""Represents a named reference to any object. Subclasses may apply restrictions though,
i.e. Heads can only point to commits."""
__slots__ = tuple()
_common_path_default = "refs"
def __init__(self, repo, path):
"""Initialize this instance
:param repo: Our parent repository
:param path:
Path relative to the .git/ directory pointing to the ref in question, i.e.
refs/heads/master"""
if not path.startswith(self._common_path_default+'/'):
raise ValueError("Cannot instantiate %r from path %s" % ( self.__class__.__name__, path ))
super(Reference, self).__init__(repo, path)
def __str__(self):
return self.name
def _get_object(self):
"""
:return:
The object our ref currently refers to. Refs can be cached, they will
always point to the actual object as it gets re-created on each query"""
# have to be dynamic here as we may be a tag which can point to anything
# Our path will be resolved to the hexsha which will be used accordingly
return Object.new_from_sha(self.repo, hex_to_bin(self.dereference_recursive(self.repo, self.path)))
def _set_object(self, ref):
"""
Set our reference to point to the given ref. It will be converted
to a specific hexsha.
If the reference does not exist, it will be created.
:note:
TypeChecking is done by the git command"""
abs_path = self._abs_path()
existed = True
if not isfile(abs_path):
existed = False
open(abs_path, 'wb').write(Object.NULL_HEX_SHA)
# END quick create
# do it safely by specifying the old value
try:
self.repo.git.update_ref(self.path, ref, (existed and self._get_object().hexsha) or None)
except:
if not existed:
os.remove(abs_path)
# END remove file on error if it didn't exist before
raise
# END exception handling
object = property(_get_object, _set_object, doc="Return the object our ref currently refers to")
@property
def name(self):
""":return: (shortest) Name of this reference - it may contain path components"""
# first two path tokens are can be removed as they are
# refs/heads or refs/tags or refs/remotes
tokens = self.path.split('/')
if len(tokens) < 3:
return self.path # could be refs/HEAD
return '/'.join(tokens[2:])
@classmethod
def create(cls, repo, path, commit='HEAD', force=False ):
"""Create a new reference.
:param repo: Repository to create the reference in
:param path:
The relative path of the reference, i.e. 'new_branch' or
feature/feature1. The path prefix 'refs/' is implied if not
given explicitly
:param commit:
Commit to which the new reference should point, defaults to the
current HEAD
:param force:
if True, force creation even if a reference with that name already exists.
Raise OSError otherwise
:return: Newly created Reference
:note: This does not alter the current HEAD, index or Working Tree"""
return cls._create(repo, path, True, commit, force)
@classmethod
def iter_items(cls, repo, common_path = None):
"""Equivalent to SymbolicReference.iter_items, but will return non-detached
references as well."""
return cls._iter_items(repo, common_path)
|