blob: 237c15ae21cd511daa8f99680e50b546b1df8087 [file] [log] [blame]
Shawn O. Pearced237b692009-04-17 18:49:50 -07001# Copyright (C) 2009 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
Mike Frysinger64477332023-08-21 21:20:32 -040016
Renaud Paquaybed8b622018-09-27 10:46:58 -070017import platform_utils
Mike Frysinger64477332023-08-21 21:20:32 -040018from repo_trace import Trace
19
Shawn O. Pearced237b692009-04-17 18:49:50 -070020
Gavin Makea2e3302023-03-11 06:46:20 +000021HEAD = "HEAD"
22R_CHANGES = "refs/changes/"
23R_HEADS = "refs/heads/"
24R_TAGS = "refs/tags/"
25R_PUB = "refs/published/"
26R_WORKTREE = "refs/worktree/"
27R_WORKTREE_M = R_WORKTREE + "m/"
28R_M = "refs/remotes/m/"
Shawn O. Pearced237b692009-04-17 18:49:50 -070029
30
Mike Frysingerd4aee652023-10-19 05:13:32 -040031class GitRefs:
Gavin Makea2e3302023-03-11 06:46:20 +000032 def __init__(self, gitdir):
33 self._gitdir = gitdir
34 self._phyref = None
35 self._symref = None
36 self._mtime = {}
Shawn O. Pearced237b692009-04-17 18:49:50 -070037
Gavin Makea2e3302023-03-11 06:46:20 +000038 @property
39 def all(self):
40 self._EnsureLoaded()
41 return self._phyref
Shawn O. Pearced237b692009-04-17 18:49:50 -070042
Gavin Makea2e3302023-03-11 06:46:20 +000043 def get(self, name):
Joanna Wanga6c52f52022-11-03 16:51:19 -040044 try:
Gavin Makea2e3302023-03-11 06:46:20 +000045 return self.all[name]
46 except KeyError:
47 return ""
48
49 def deleted(self, name):
50 if self._phyref is not None:
51 if name in self._phyref:
52 del self._phyref[name]
53
54 if name in self._symref:
55 del self._symref[name]
56
57 if name in self._mtime:
58 del self._mtime[name]
59
60 def symref(self, name):
61 try:
62 self._EnsureLoaded()
63 return self._symref[name]
64 except KeyError:
65 return ""
66
67 def _EnsureLoaded(self):
68 if self._phyref is None or self._NeedUpdate():
69 self._LoadAll()
70
71 def _NeedUpdate(self):
72 with Trace(": scan refs %s", self._gitdir):
73 for name, mtime in self._mtime.items():
74 try:
75 if mtime != os.path.getmtime(
76 os.path.join(self._gitdir, name)
77 ):
78 return True
79 except OSError:
80 return True
81 return False
82
83 def _LoadAll(self):
84 with Trace(": load refs %s", self._gitdir):
85 self._phyref = {}
86 self._symref = {}
87 self._mtime = {}
88
89 self._ReadPackedRefs()
90 self._ReadLoose("refs/")
91 self._ReadLoose1(os.path.join(self._gitdir, HEAD), HEAD)
92
93 scan = self._symref
94 attempts = 0
95 while scan and attempts < 5:
96 scan_next = {}
97 for name, dest in scan.items():
98 if dest in self._phyref:
99 self._phyref[name] = self._phyref[dest]
100 else:
101 scan_next[name] = dest
102 scan = scan_next
103 attempts += 1
104
105 def _ReadPackedRefs(self):
106 path = os.path.join(self._gitdir, "packed-refs")
107 try:
Jason R. Coombs034950b2023-10-20 23:32:02 +0545108 fd = open(path)
Gavin Makea2e3302023-03-11 06:46:20 +0000109 mtime = os.path.getmtime(path)
Joanna Wanga6c52f52022-11-03 16:51:19 -0400110 except OSError:
Gavin Makea2e3302023-03-11 06:46:20 +0000111 return
112 try:
113 for line in fd:
114 line = str(line)
115 if line[0] == "#":
116 continue
117 if line[0] == "^":
118 continue
Shawn O. Pearced237b692009-04-17 18:49:50 -0700119
Gavin Makea2e3302023-03-11 06:46:20 +0000120 line = line[:-1]
121 p = line.split(" ")
122 ref_id = p[0]
123 name = p[1]
Shawn O. Pearcead3193a2009-04-18 09:54:51 -0700124
Gavin Makea2e3302023-03-11 06:46:20 +0000125 self._phyref[name] = ref_id
126 finally:
127 fd.close()
128 self._mtime["packed-refs"] = mtime
Shawn O. Pearced237b692009-04-17 18:49:50 -0700129
Gavin Makea2e3302023-03-11 06:46:20 +0000130 def _ReadLoose(self, prefix):
131 base = os.path.join(self._gitdir, prefix)
132 for name in platform_utils.listdir(base):
133 p = os.path.join(base, name)
134 # We don't implement the full ref validation algorithm, just the
135 # simple rules that would show up in local filesystems.
136 # https://git-scm.com/docs/git-check-ref-format
137 if name.startswith(".") or name.endswith(".lock"):
138 pass
139 elif platform_utils.isdir(p):
140 self._mtime[prefix] = os.path.getmtime(base)
141 self._ReadLoose(prefix + name + "/")
142 else:
143 self._ReadLoose1(p, prefix + name)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700144
Gavin Makea2e3302023-03-11 06:46:20 +0000145 def _ReadLoose1(self, path, name):
146 try:
147 with open(path) as fd:
148 mtime = os.path.getmtime(path)
149 ref_id = fd.readline()
150 except (OSError, UnicodeError):
151 return
Shawn O. Pearced237b692009-04-17 18:49:50 -0700152
Gavin Makea2e3302023-03-11 06:46:20 +0000153 try:
154 ref_id = ref_id.decode()
155 except AttributeError:
156 pass
157 if not ref_id:
158 return
159 ref_id = ref_id[:-1]
Shawn O. Pearced237b692009-04-17 18:49:50 -0700160
Gavin Makea2e3302023-03-11 06:46:20 +0000161 if ref_id.startswith("ref: "):
162 self._symref[name] = ref_id[5:]
163 else:
164 self._phyref[name] = ref_id
165 self._mtime[name] = mtime