summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
Diffstat (limited to 'project.py')
-rw-r--r--project.py146
1 files changed, 102 insertions, 44 deletions
diff --git a/project.py b/project.py
index d07b5216..460bf315 100644
--- a/project.py
+++ b/project.py
@@ -231,14 +231,40 @@ class _CopyFile:
231 except IOError: 231 except IOError:
232 _error('Cannot copy file %s to %s', src, dest) 232 _error('Cannot copy file %s to %s', src, dest)
233 233
234class _LinkFile:
235 def __init__(self, src, dest, abssrc, absdest):
236 self.src = src
237 self.dest = dest
238 self.abs_src = abssrc
239 self.abs_dest = absdest
240
241 def _Link(self):
242 src = self.abs_src
243 dest = self.abs_dest
244 # link file if it does not exist or is out of date
245 if not os.path.islink(dest) or os.readlink(dest) != src:
246 try:
247 # remove existing file first, since it might be read-only
248 if os.path.exists(dest):
249 os.remove(dest)
250 else:
251 dest_dir = os.path.dirname(dest)
252 if not os.path.isdir(dest_dir):
253 os.makedirs(dest_dir)
254 os.symlink(src, dest)
255 except IOError:
256 _error('Cannot link file %s to %s', src, dest)
257
234class RemoteSpec(object): 258class RemoteSpec(object):
235 def __init__(self, 259 def __init__(self,
236 name, 260 name,
237 url = None, 261 url = None,
238 review = None): 262 review = None,
263 revision = None):
239 self.name = name 264 self.name = name
240 self.url = url 265 self.url = url
241 self.review = review 266 self.review = review
267 self.revision = revision
242 268
243class RepoHook(object): 269class RepoHook(object):
244 """A RepoHook contains information about a script to run as a hook. 270 """A RepoHook contains information about a script to run as a hook.
@@ -414,7 +440,8 @@ class RepoHook(object):
414 # and convert to a HookError w/ just the failing traceback. 440 # and convert to a HookError w/ just the failing traceback.
415 context = {} 441 context = {}
416 try: 442 try:
417 execfile(self._script_fullpath, context) 443 exec(compile(open(self._script_fullpath).read(),
444 self._script_fullpath, 'exec'), context)
418 except Exception: 445 except Exception:
419 raise HookError('%s\nFailed to import %s hook; see traceback above.' % ( 446 raise HookError('%s\nFailed to import %s hook; see traceback above.' % (
420 traceback.format_exc(), self._hook_type)) 447 traceback.format_exc(), self._hook_type))
@@ -555,6 +582,7 @@ class Project(object):
555 582
556 self.snapshots = {} 583 self.snapshots = {}
557 self.copyfiles = [] 584 self.copyfiles = []
585 self.linkfiles = []
558 self.annotations = [] 586 self.annotations = []
559 self.config = GitConfig.ForRepository( 587 self.config = GitConfig.ForRepository(
560 gitdir = self.gitdir, 588 gitdir = self.gitdir,
@@ -1040,7 +1068,7 @@ class Project(object):
1040 except OSError as e: 1068 except OSError as e:
1041 print("warn: Cannot remove archive %s: " 1069 print("warn: Cannot remove archive %s: "
1042 "%s" % (tarpath, str(e)), file=sys.stderr) 1070 "%s" % (tarpath, str(e)), file=sys.stderr)
1043 self._CopyFiles() 1071 self._CopyAndLinkFiles()
1044 return True 1072 return True
1045 1073
1046 if is_new is None: 1074 if is_new is None:
@@ -1078,17 +1106,12 @@ class Project(object):
1078 elif self.manifest.default.sync_c: 1106 elif self.manifest.default.sync_c:
1079 current_branch_only = True 1107 current_branch_only = True
1080 1108
1081 is_sha1 = False 1109 has_sha1 = ID_RE.match(self.revisionExpr) and self._CheckForSha1()
1082 if ID_RE.match(self.revisionExpr) is not None: 1110 if (not has_sha1 #Need to fetch since we don't already have this revision
1083 is_sha1 = True 1111 and not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1084 if is_sha1 and self._CheckForSha1(): 1112 current_branch_only=current_branch_only,
1085 # Don't need to fetch since we already have this revision 1113 no_tags=no_tags)):
1086 return True 1114 return False
1087
1088 if not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1089 current_branch_only=current_branch_only,
1090 no_tags=no_tags):
1091 return False
1092 1115
1093 if self.worktree: 1116 if self.worktree:
1094 self._InitMRef() 1117 self._InitMRef()
@@ -1103,9 +1126,11 @@ class Project(object):
1103 def PostRepoUpgrade(self): 1126 def PostRepoUpgrade(self):
1104 self._InitHooks() 1127 self._InitHooks()
1105 1128
1106 def _CopyFiles(self): 1129 def _CopyAndLinkFiles(self):
1107 for copyfile in self.copyfiles: 1130 for copyfile in self.copyfiles:
1108 copyfile._Copy() 1131 copyfile._Copy()
1132 for linkfile in self.linkfiles:
1133 linkfile._Link()
1109 1134
1110 def GetCommitRevisionId(self): 1135 def GetCommitRevisionId(self):
1111 """Get revisionId of a commit. 1136 """Get revisionId of a commit.
@@ -1152,7 +1177,7 @@ class Project(object):
1152 1177
1153 def _doff(): 1178 def _doff():
1154 self._FastForward(revid) 1179 self._FastForward(revid)
1155 self._CopyFiles() 1180 self._CopyAndLinkFiles()
1156 1181
1157 head = self.work_git.GetHead() 1182 head = self.work_git.GetHead()
1158 if head.startswith(R_HEADS): 1183 if head.startswith(R_HEADS):
@@ -1188,7 +1213,7 @@ class Project(object):
1188 except GitError as e: 1213 except GitError as e:
1189 syncbuf.fail(self, e) 1214 syncbuf.fail(self, e)
1190 return 1215 return
1191 self._CopyFiles() 1216 self._CopyAndLinkFiles()
1192 return 1217 return
1193 1218
1194 if head == revid: 1219 if head == revid:
@@ -1210,7 +1235,7 @@ class Project(object):
1210 except GitError as e: 1235 except GitError as e:
1211 syncbuf.fail(self, e) 1236 syncbuf.fail(self, e)
1212 return 1237 return
1213 self._CopyFiles() 1238 self._CopyAndLinkFiles()
1214 return 1239 return
1215 1240
1216 upstream_gain = self._revlist(not_rev(HEAD), revid) 1241 upstream_gain = self._revlist(not_rev(HEAD), revid)
@@ -1283,12 +1308,12 @@ class Project(object):
1283 if cnt_mine > 0 and self.rebase: 1308 if cnt_mine > 0 and self.rebase:
1284 def _dorebase(): 1309 def _dorebase():
1285 self._Rebase(upstream = '%s^1' % last_mine, onto = revid) 1310 self._Rebase(upstream = '%s^1' % last_mine, onto = revid)
1286 self._CopyFiles() 1311 self._CopyAndLinkFiles()
1287 syncbuf.later2(self, _dorebase) 1312 syncbuf.later2(self, _dorebase)
1288 elif local_changes: 1313 elif local_changes:
1289 try: 1314 try:
1290 self._ResetHard(revid) 1315 self._ResetHard(revid)
1291 self._CopyFiles() 1316 self._CopyAndLinkFiles()
1292 except GitError as e: 1317 except GitError as e:
1293 syncbuf.fail(self, e) 1318 syncbuf.fail(self, e)
1294 return 1319 return
@@ -1301,6 +1326,12 @@ class Project(object):
1301 abssrc = os.path.join(self.worktree, src) 1326 abssrc = os.path.join(self.worktree, src)
1302 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest)) 1327 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
1303 1328
1329 def AddLinkFile(self, src, dest, absdest):
1330 # dest should already be an absolute path, but src is project relative
1331 # make src an absolute path
1332 abssrc = os.path.join(self.worktree, src)
1333 self.linkfiles.append(_LinkFile(src, dest, abssrc, absdest))
1334
1304 def AddAnnotation(self, name, value, keep): 1335 def AddAnnotation(self, name, value, keep):
1305 self.annotations.append(_Annotation(name, value, keep)) 1336 self.annotations.append(_Annotation(name, value, keep))
1306 1337
@@ -1629,7 +1660,8 @@ class Project(object):
1629 1660
1630 remote = RemoteSpec(self.remote.name, 1661 remote = RemoteSpec(self.remote.name,
1631 url = url, 1662 url = url,
1632 review = self.remote.review) 1663 review = self.remote.review,
1664 revision = self.remote.revision)
1633 subproject = Project(manifest = self.manifest, 1665 subproject = Project(manifest = self.manifest,
1634 name = name, 1666 name = name,
1635 remote = remote, 1667 remote = remote,
@@ -1674,6 +1706,7 @@ class Project(object):
1674 if command.Wait() != 0: 1706 if command.Wait() != 0:
1675 raise GitError('git archive %s: %s' % (self.name, command.stderr)) 1707 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1676 1708
1709
1677 def _RemoteFetch(self, name=None, 1710 def _RemoteFetch(self, name=None,
1678 current_branch_only=False, 1711 current_branch_only=False,
1679 initial=False, 1712 initial=False,
@@ -1683,11 +1716,17 @@ class Project(object):
1683 1716
1684 is_sha1 = False 1717 is_sha1 = False
1685 tag_name = None 1718 tag_name = None
1719 depth = None
1720
1721 # The depth should not be used when fetching to a mirror because
1722 # it will result in a shallow repository that cannot be cloned or
1723 # fetched from.
1724 if not self.manifest.IsMirror:
1725 if self.clone_depth:
1726 depth = self.clone_depth
1727 else:
1728 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1686 1729
1687 if self.clone_depth:
1688 depth = self.clone_depth
1689 else:
1690 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1691 if depth: 1730 if depth:
1692 current_branch_only = True 1731 current_branch_only = True
1693 1732
@@ -1763,26 +1802,37 @@ class Project(object):
1763 cmd.append('--update-head-ok') 1802 cmd.append('--update-head-ok')
1764 cmd.append(name) 1803 cmd.append(name)
1765 1804
1805 # If using depth then we should not get all the tags since they may
1806 # be outside of the depth.
1807 if no_tags or depth:
1808 cmd.append('--no-tags')
1809 else:
1810 cmd.append('--tags')
1811
1812 spec = []
1766 if not current_branch_only: 1813 if not current_branch_only:
1767 # Fetch whole repo 1814 # Fetch whole repo
1768 # If using depth then we should not get all the tags since they may 1815 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
1769 # be outside of the depth.
1770 if no_tags or depth:
1771 cmd.append('--no-tags')
1772 else:
1773 cmd.append('--tags')
1774
1775 cmd.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
1776 elif tag_name is not None: 1816 elif tag_name is not None:
1777 cmd.append('tag') 1817 spec.append('tag')
1778 cmd.append(tag_name) 1818 spec.append(tag_name)
1779 else: 1819 else:
1780 branch = self.revisionExpr 1820 branch = self.revisionExpr
1781 if is_sha1: 1821 if is_sha1:
1782 branch = self.upstream 1822 branch = self.upstream
1783 if branch.startswith(R_HEADS): 1823 if branch.startswith(R_HEADS):
1784 branch = branch[len(R_HEADS):] 1824 branch = branch[len(R_HEADS):]
1785 cmd.append(str((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch))) 1825 spec.append(str((u'+refs/heads/%s:' % branch) + remote.ToLocal('refs/heads/%s' % branch)))
1826 cmd.extend(spec)
1827
1828 shallowfetch = self.config.GetString('repo.shallowfetch')
1829 if shallowfetch and shallowfetch != ' '.join(spec):
1830 GitCommand(self, ['fetch', '--unshallow', name] + shallowfetch.split(),
1831 bare=True, ssh_proxy=ssh_proxy).Wait()
1832 if depth:
1833 self.config.SetString('repo.shallowfetch', ' '.join(spec))
1834 else:
1835 self.config.SetString('repo.shallowfetch', None)
1786 1836
1787 ok = False 1837 ok = False
1788 for _i in range(2): 1838 for _i in range(2):
@@ -1801,7 +1851,7 @@ class Project(object):
1801 # Ensure that some refs exist. Otherwise, we probably aren't looking 1851 # Ensure that some refs exist. Otherwise, we probably aren't looking
1802 # at a real git repository and may have a bad url. 1852 # at a real git repository and may have a bad url.
1803 if not self.bare_ref.all: 1853 if not self.bare_ref.all:
1804 ok = False 1854 ok = False
1805 1855
1806 if alt_dir: 1856 if alt_dir:
1807 if old_packed != '': 1857 if old_packed != '':
@@ -2147,7 +2197,7 @@ class Project(object):
2147 symlink_dirs = ['hooks', 'objects', 'rr-cache', 'svn'] 2197 symlink_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
2148 if share_refs: 2198 if share_refs:
2149 # These objects can only be used by a single working tree. 2199 # These objects can only be used by a single working tree.
2150 symlink_files += ['config', 'packed-refs'] 2200 symlink_files += ['config', 'packed-refs', 'shallow']
2151 symlink_dirs += ['logs', 'refs'] 2201 symlink_dirs += ['logs', 'refs']
2152 to_symlink = symlink_files + symlink_dirs 2202 to_symlink = symlink_files + symlink_dirs
2153 2203
@@ -2167,6 +2217,14 @@ class Project(object):
2167 if name in symlink_dirs and not os.path.lexists(src): 2217 if name in symlink_dirs and not os.path.lexists(src):
2168 os.makedirs(src) 2218 os.makedirs(src)
2169 2219
2220 # If the source file doesn't exist, ensure the destination
2221 # file doesn't either.
2222 if name in symlink_files and not os.path.lexists(src):
2223 try:
2224 os.remove(dst)
2225 except OSError:
2226 pass
2227
2170 if name in to_symlink: 2228 if name in to_symlink:
2171 os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst) 2229 os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst)
2172 elif copy_all and not os.path.islink(dst): 2230 elif copy_all and not os.path.islink(dst):
@@ -2195,7 +2253,7 @@ class Project(object):
2195 if GitCommand(self, cmd).Wait() != 0: 2253 if GitCommand(self, cmd).Wait() != 0:
2196 raise GitError("cannot initialize work tree") 2254 raise GitError("cannot initialize work tree")
2197 2255
2198 self._CopyFiles() 2256 self._CopyAndLinkFiles()
2199 2257
2200 def _gitdir_path(self, path): 2258 def _gitdir_path(self, path):
2201 return os.path.realpath(os.path.join(self.gitdir, path)) 2259 return os.path.realpath(os.path.join(self.gitdir, path))
@@ -2287,8 +2345,8 @@ class Project(object):
2287 out = iter(out[:-1].split('\0')) # pylint: disable=W1401 2345 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
2288 while out: 2346 while out:
2289 try: 2347 try:
2290 info = out.next() 2348 info = next(out)
2291 path = out.next() 2349 path = next(out)
2292 except StopIteration: 2350 except StopIteration:
2293 break 2351 break
2294 2352
@@ -2314,7 +2372,7 @@ class Project(object):
2314 info = _Info(path, *info) 2372 info = _Info(path, *info)
2315 if info.status in ('R', 'C'): 2373 if info.status in ('R', 'C'):
2316 info.src_path = info.path 2374 info.src_path = info.path
2317 info.path = out.next() 2375 info.path = next(out)
2318 r[info.path] = info 2376 r[info.path] = info
2319 return r 2377 return r
2320 finally: 2378 finally:
@@ -2327,8 +2385,8 @@ class Project(object):
2327 path = os.path.join(self._project.worktree, '.git', HEAD) 2385 path = os.path.join(self._project.worktree, '.git', HEAD)
2328 try: 2386 try:
2329 fd = open(path, 'rb') 2387 fd = open(path, 'rb')
2330 except IOError: 2388 except IOError as e:
2331 raise NoManifestException(path) 2389 raise NoManifestException(path, str(e))
2332 try: 2390 try:
2333 line = fd.read() 2391 line = fd.read()
2334 finally: 2392 finally: