| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 1 | # 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 |  | 
 | 15 | import os | 
| Mike Frysinger | 8a11f6f | 2019-08-27 00:26:15 -0400 | [diff] [blame] | 16 | from repo_trace import Trace | 
| Renaud Paquay | bed8b62 | 2018-09-27 10:46:58 -0700 | [diff] [blame] | 17 | import platform_utils | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 18 |  | 
| David Pursehouse | 54a4e60 | 2020-02-12 14:31:05 +0900 | [diff] [blame] | 19 | HEAD = 'HEAD' | 
| Zac Livingston | 9ead97b | 2017-06-13 08:29:04 -0600 | [diff] [blame] | 20 | R_CHANGES = 'refs/changes/' | 
| David Pursehouse | 54a4e60 | 2020-02-12 14:31:05 +0900 | [diff] [blame] | 21 | R_HEADS = 'refs/heads/' | 
 | 22 | R_TAGS = 'refs/tags/' | 
 | 23 | R_PUB = 'refs/published/' | 
| Mike Frysinger | 21b7fbe | 2020-02-26 23:53:36 -0500 | [diff] [blame] | 24 | R_WORKTREE = 'refs/worktree/' | 
 | 25 | R_WORKTREE_M = R_WORKTREE + 'm/' | 
| David Pursehouse | 54a4e60 | 2020-02-12 14:31:05 +0900 | [diff] [blame] | 26 | R_M = 'refs/remotes/m/' | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 27 |  | 
 | 28 |  | 
 | 29 | class GitRefs(object): | 
 | 30 |   def __init__(self, gitdir): | 
 | 31 |     self._gitdir = gitdir | 
 | 32 |     self._phyref = None | 
 | 33 |     self._symref = None | 
 | 34 |     self._mtime = {} | 
 | 35 |  | 
 | 36 |   @property | 
 | 37 |   def all(self): | 
| Shawn O. Pearce | 0f3dd23 | 2009-04-17 20:32:44 -0700 | [diff] [blame] | 38 |     self._EnsureLoaded() | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 39 |     return self._phyref | 
 | 40 |  | 
 | 41 |   def get(self, name): | 
 | 42 |     try: | 
 | 43 |       return self.all[name] | 
 | 44 |     except KeyError: | 
 | 45 |       return '' | 
 | 46 |  | 
| Shawn O. Pearce | fbcde47 | 2009-04-17 20:58:02 -0700 | [diff] [blame] | 47 |   def deleted(self, name): | 
 | 48 |     if self._phyref is not None: | 
 | 49 |       if name in self._phyref: | 
 | 50 |         del self._phyref[name] | 
 | 51 |  | 
 | 52 |       if name in self._symref: | 
 | 53 |         del self._symref[name] | 
 | 54 |  | 
 | 55 |       if name in self._mtime: | 
 | 56 |         del self._mtime[name] | 
 | 57 |  | 
| Shawn O. Pearce | 0f3dd23 | 2009-04-17 20:32:44 -0700 | [diff] [blame] | 58 |   def symref(self, name): | 
 | 59 |     try: | 
 | 60 |       self._EnsureLoaded() | 
 | 61 |       return self._symref[name] | 
 | 62 |     except KeyError: | 
 | 63 |       return '' | 
 | 64 |  | 
 | 65 |   def _EnsureLoaded(self): | 
 | 66 |     if self._phyref is None or self._NeedUpdate(): | 
 | 67 |       self._LoadAll() | 
 | 68 |  | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 69 |   def _NeedUpdate(self): | 
| Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 70 |     Trace(': scan refs %s', self._gitdir) | 
 | 71 |  | 
| Chirayu Desai | 217ea7d | 2013-03-01 19:14:38 +0530 | [diff] [blame] | 72 |     for name, mtime in self._mtime.items(): | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 73 |       try: | 
 | 74 |         if mtime != os.path.getmtime(os.path.join(self._gitdir, name)): | 
 | 75 |           return True | 
 | 76 |       except OSError: | 
 | 77 |         return True | 
 | 78 |     return False | 
 | 79 |  | 
 | 80 |   def _LoadAll(self): | 
| Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 81 |     Trace(': load refs %s', self._gitdir) | 
 | 82 |  | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 83 |     self._phyref = {} | 
 | 84 |     self._symref = {} | 
 | 85 |     self._mtime = {} | 
 | 86 |  | 
 | 87 |     self._ReadPackedRefs() | 
 | 88 |     self._ReadLoose('refs/') | 
 | 89 |     self._ReadLoose1(os.path.join(self._gitdir, HEAD), HEAD) | 
 | 90 |  | 
 | 91 |     scan = self._symref | 
 | 92 |     attempts = 0 | 
 | 93 |     while scan and attempts < 5: | 
 | 94 |       scan_next = {} | 
| Chirayu Desai | 217ea7d | 2013-03-01 19:14:38 +0530 | [diff] [blame] | 95 |       for name, dest in scan.items(): | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 96 |         if dest in self._phyref: | 
 | 97 |           self._phyref[name] = self._phyref[dest] | 
 | 98 |         else: | 
 | 99 |           scan_next[name] = dest | 
 | 100 |       scan = scan_next | 
 | 101 |       attempts += 1 | 
 | 102 |  | 
 | 103 |   def _ReadPackedRefs(self): | 
 | 104 |     path = os.path.join(self._gitdir, 'packed-refs') | 
 | 105 |     try: | 
| Chirayu Desai | 0eb35cb | 2013-11-19 18:46:29 +0530 | [diff] [blame] | 106 |       fd = open(path, 'r') | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 107 |       mtime = os.path.getmtime(path) | 
 | 108 |     except IOError: | 
 | 109 |       return | 
 | 110 |     except OSError: | 
 | 111 |       return | 
 | 112 |     try: | 
 | 113 |       for line in fd: | 
| Chirayu Desai | 217ea7d | 2013-03-01 19:14:38 +0530 | [diff] [blame] | 114 |         line = str(line) | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 115 |         if line[0] == '#': | 
 | 116 |           continue | 
 | 117 |         if line[0] == '^': | 
 | 118 |           continue | 
 | 119 |  | 
 | 120 |         line = line[:-1] | 
 | 121 |         p = line.split(' ') | 
| David Pursehouse | 8a68ff9 | 2012-09-24 12:15:13 +0900 | [diff] [blame] | 122 |         ref_id = p[0] | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 123 |         name = p[1] | 
 | 124 |  | 
| David Pursehouse | 8a68ff9 | 2012-09-24 12:15:13 +0900 | [diff] [blame] | 125 |         self._phyref[name] = ref_id | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 126 |     finally: | 
 | 127 |       fd.close() | 
 | 128 |     self._mtime['packed-refs'] = mtime | 
 | 129 |  | 
 | 130 |   def _ReadLoose(self, prefix): | 
 | 131 |     base = os.path.join(self._gitdir, prefix) | 
| Renaud Paquay | bed8b62 | 2018-09-27 10:46:58 -0700 | [diff] [blame] | 132 |     for name in platform_utils.listdir(base): | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 133 |       p = os.path.join(base, name) | 
| Mike Frysinger | 6d1faa1 | 2021-02-27 21:27:04 -0500 | [diff] [blame] | 134 |       # We don't implement the full ref validation algorithm, just the simple | 
 | 135 |       # 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): | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 140 |         self._mtime[prefix] = os.path.getmtime(base) | 
 | 141 |         self._ReadLoose(prefix + name + '/') | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 142 |       else: | 
 | 143 |         self._ReadLoose1(p, prefix + name) | 
 | 144 |  | 
 | 145 |   def _ReadLoose1(self, path, name): | 
 | 146 |     try: | 
| Mike Frysinger | 3164d40 | 2019-11-11 05:40:22 -0500 | [diff] [blame] | 147 |       with open(path) as fd: | 
| Shawn O. Pearce | cc14fa9 | 2011-11-29 12:32:56 -0800 | [diff] [blame] | 148 |         mtime = os.path.getmtime(path) | 
| David Pursehouse | 8a68ff9 | 2012-09-24 12:15:13 +0900 | [diff] [blame] | 149 |         ref_id = fd.readline() | 
| Mike Frysinger | 6d1faa1 | 2021-02-27 21:27:04 -0500 | [diff] [blame] | 150 |     except (OSError, UnicodeError): | 
| Mike Frysinger | 3164d40 | 2019-11-11 05:40:22 -0500 | [diff] [blame] | 151 |       return | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 152 |  | 
| Chirayu Desai | 217ea7d | 2013-03-01 19:14:38 +0530 | [diff] [blame] | 153 |     try: | 
 | 154 |       ref_id = ref_id.decode() | 
 | 155 |     except AttributeError: | 
 | 156 |       pass | 
| David Pursehouse | 8a68ff9 | 2012-09-24 12:15:13 +0900 | [diff] [blame] | 157 |     if not ref_id: | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 158 |       return | 
| David Pursehouse | 8a68ff9 | 2012-09-24 12:15:13 +0900 | [diff] [blame] | 159 |     ref_id = ref_id[:-1] | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 160 |  | 
| David Pursehouse | 8a68ff9 | 2012-09-24 12:15:13 +0900 | [diff] [blame] | 161 |     if ref_id.startswith('ref: '): | 
 | 162 |       self._symref[name] = ref_id[5:] | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 163 |     else: | 
| David Pursehouse | 8a68ff9 | 2012-09-24 12:15:13 +0900 | [diff] [blame] | 164 |       self._phyref[name] = ref_id | 
| Shawn O. Pearce | d237b69 | 2009-04-17 18:49:50 -0700 | [diff] [blame] | 165 |     self._mtime[name] = mtime |