summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
Diffstat (limited to 'project.py')
-rw-r--r--project.py199
1 files changed, 12 insertions, 187 deletions
diff --git a/project.py b/project.py
index c5ee50fc..cdb4ecfd 100644
--- a/project.py
+++ b/project.py
@@ -22,11 +22,10 @@ import shutil
22import stat 22import stat
23import subprocess 23import subprocess
24import sys 24import sys
25import tempfile
26import time 25import time
27 26
28from color import Coloring 27from color import Coloring
29from git_command import GitCommand 28from git_command import GitCommand, git_require
30from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE 29from git_config import GitConfig, IsId, GetSchemeFromUrl, ID_RE
31from error import GitError, HookError, UploadError 30from error import GitError, HookError, UploadError
32from error import ManifestInvalidRevisionError 31from error import ManifestInvalidRevisionError
@@ -485,28 +484,7 @@ class Project(object):
485 rebase = True, 484 rebase = True,
486 groups = None, 485 groups = None,
487 sync_c = False, 486 sync_c = False,
488 upstream = None, 487 upstream = None):
489 parent = None,
490 is_derived = False):
491 """Init a Project object.
492
493 Args:
494 manifest: The XmlManifest object.
495 name: The `name` attribute of manifest.xml's project element.
496 remote: RemoteSpec object specifying its remote's properties.
497 gitdir: Absolute path of git directory.
498 worktree: Absolute path of git working tree.
499 relpath: Relative path of git working tree to repo's top directory.
500 revisionExpr: The `revision` attribute of manifest.xml's project element.
501 revisionId: git commit id for checking out.
502 rebase: The `rebase` attribute of manifest.xml's project element.
503 groups: The `groups` attribute of manifest.xml's project element.
504 sync_c: The `sync-c` attribute of manifest.xml's project element.
505 upstream: The `upstream` attribute of manifest.xml's project element.
506 parent: The parent Project object.
507 is_derived: False if the project was explicitly defined in the manifest;
508 True if the project is a discovered submodule.
509 """
510 self.manifest = manifest 488 self.manifest = manifest
511 self.name = name 489 self.name = name
512 self.remote = remote 490 self.remote = remote
@@ -529,9 +507,6 @@ class Project(object):
529 self.groups = groups 507 self.groups = groups
530 self.sync_c = sync_c 508 self.sync_c = sync_c
531 self.upstream = upstream 509 self.upstream = upstream
532 self.parent = parent
533 self.is_derived = is_derived
534 self.subprojects = []
535 510
536 self.snapshots = {} 511 self.snapshots = {}
537 self.copyfiles = [] 512 self.copyfiles = []
@@ -552,14 +527,6 @@ class Project(object):
552 self.enabled_repo_hooks = [] 527 self.enabled_repo_hooks = []
553 528
554 @property 529 @property
555 def Registered(self):
556 return self.parent and not self.is_derived
557
558 @property
559 def Derived(self):
560 return self.is_derived
561
562 @property
563 def Exists(self): 530 def Exists(self):
564 return os.path.isdir(self.gitdir) 531 return os.path.isdir(self.gitdir)
565 532
@@ -1045,6 +1012,10 @@ class Project(object):
1045 self.CleanPublishedCache(all_refs) 1012 self.CleanPublishedCache(all_refs)
1046 revid = self.GetRevisionId(all_refs) 1013 revid = self.GetRevisionId(all_refs)
1047 1014
1015 def _doff():
1016 self._FastForward(revid)
1017 self._CopyFiles()
1018
1048 self._InitWorkTree() 1019 self._InitWorkTree()
1049 head = self.work_git.GetHead() 1020 head = self.work_git.GetHead()
1050 if head.startswith(R_HEADS): 1021 if head.startswith(R_HEADS):
@@ -1123,9 +1094,6 @@ class Project(object):
1123 # All published commits are merged, and thus we are a 1094 # All published commits are merged, and thus we are a
1124 # strict subset. We can fast-forward safely. 1095 # strict subset. We can fast-forward safely.
1125 # 1096 #
1126 def _doff():
1127 self._FastForward(revid)
1128 self._CopyFiles()
1129 syncbuf.later1(self, _doff) 1097 syncbuf.later1(self, _doff)
1130 return 1098 return
1131 1099
@@ -1188,9 +1156,6 @@ class Project(object):
1188 syncbuf.fail(self, e) 1156 syncbuf.fail(self, e)
1189 return 1157 return
1190 else: 1158 else:
1191 def _doff():
1192 self._FastForward(revid)
1193 self._CopyFiles()
1194 syncbuf.later1(self, _doff) 1159 syncbuf.later1(self, _doff)
1195 1160
1196 def AddCopyFile(self, src, dest, absdest): 1161 def AddCopyFile(self, src, dest, absdest):
@@ -1403,150 +1368,6 @@ class Project(object):
1403 return kept 1368 return kept
1404 1369
1405 1370
1406## Submodule Management ##
1407
1408 def GetRegisteredSubprojects(self):
1409 result = []
1410 def rec(subprojects):
1411 if not subprojects:
1412 return
1413 result.extend(subprojects)
1414 for p in subprojects:
1415 rec(p.subprojects)
1416 rec(self.subprojects)
1417 return result
1418
1419 def _GetSubmodules(self):
1420 # Unfortunately we cannot call `git submodule status --recursive` here
1421 # because the working tree might not exist yet, and it cannot be used
1422 # without a working tree in its current implementation.
1423
1424 def get_submodules(gitdir, rev):
1425 # Parse .gitmodules for submodule sub_paths and sub_urls
1426 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1427 if not sub_paths:
1428 return []
1429 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1430 # revision of submodule repository
1431 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1432 submodules = []
1433 for sub_path, sub_url in zip(sub_paths, sub_urls):
1434 try:
1435 sub_rev = sub_revs[sub_path]
1436 except KeyError:
1437 # Ignore non-exist submodules
1438 continue
1439 submodules.append((sub_rev, sub_path, sub_url))
1440 return submodules
1441
1442 re_path = re.compile(r'submodule.(\w+).path')
1443 re_url = re.compile(r'submodule.(\w+).url')
1444 def parse_gitmodules(gitdir, rev):
1445 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1446 try:
1447 p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True,
1448 bare = True, gitdir = gitdir)
1449 except GitError:
1450 return [], []
1451 if p.Wait() != 0:
1452 return [], []
1453
1454 gitmodules_lines = []
1455 fd, temp_gitmodules_path = tempfile.mkstemp()
1456 try:
1457 os.write(fd, p.stdout)
1458 os.close(fd)
1459 cmd = ['config', '--file', temp_gitmodules_path, '--list']
1460 p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True,
1461 bare = True, gitdir = gitdir)
1462 if p.Wait() != 0:
1463 return [], []
1464 gitmodules_lines = p.stdout.split('\n')
1465 except GitError:
1466 return [], []
1467 finally:
1468 os.remove(temp_gitmodules_path)
1469
1470 names = set()
1471 paths = {}
1472 urls = {}
1473 for line in gitmodules_lines:
1474 if not line:
1475 continue
1476 key, value = line.split('=')
1477 m = re_path.match(key)
1478 if m:
1479 names.add(m.group(1))
1480 paths[m.group(1)] = value
1481 continue
1482 m = re_url.match(key)
1483 if m:
1484 names.add(m.group(1))
1485 urls[m.group(1)] = value
1486 continue
1487 names = sorted(names)
1488 return [paths[name] for name in names], [urls[name] for name in names]
1489
1490 def git_ls_tree(gitdir, rev, paths):
1491 cmd = ['ls-tree', rev, '--']
1492 cmd.extend(paths)
1493 try:
1494 p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True,
1495 bare = True, gitdir = gitdir)
1496 except GitError:
1497 return []
1498 if p.Wait() != 0:
1499 return []
1500 objects = {}
1501 for line in p.stdout.split('\n'):
1502 if not line.strip():
1503 continue
1504 object_rev, object_path = line.split()[2:4]
1505 objects[object_path] = object_rev
1506 return objects
1507
1508 try:
1509 rev = self.GetRevisionId()
1510 except GitError:
1511 return []
1512 return get_submodules(self.gitdir, rev)
1513
1514 def GetDerivedSubprojects(self):
1515 result = []
1516 if not self.Exists:
1517 # If git repo does not exist yet, querying its submodules will
1518 # mess up its states; so return here.
1519 return result
1520 for rev, path, url in self._GetSubmodules():
1521 name = self.manifest.GetSubprojectName(self, path)
1522 project = self.manifest.projects.get(name)
1523 if project and project.Registered:
1524 # If it has been registered, skip it because we are searching
1525 # derived subprojects, but search for its derived subprojects.
1526 result.extend(project.GetDerivedSubprojects())
1527 continue
1528 relpath, worktree, gitdir = self.manifest.GetSubprojectPaths(self, path)
1529 remote = RemoteSpec(self.remote.name,
1530 url = url,
1531 review = self.remote.review)
1532 subproject = Project(manifest = self.manifest,
1533 name = name,
1534 remote = remote,
1535 gitdir = gitdir,
1536 worktree = worktree,
1537 relpath = relpath,
1538 revisionExpr = self.revisionExpr,
1539 revisionId = rev,
1540 rebase = self.rebase,
1541 groups = self.groups,
1542 sync_c = self.sync_c,
1543 parent = self,
1544 is_derived = True)
1545 result.append(subproject)
1546 result.extend(subproject.GetDerivedSubprojects())
1547 return result
1548
1549
1550## Direct Git Commands ## 1371## Direct Git Commands ##
1551 1372
1552 def _RemoteFetch(self, name=None, 1373 def _RemoteFetch(self, name=None,
@@ -2013,7 +1834,8 @@ class Project(object):
2013 if p.Wait() == 0: 1834 if p.Wait() == 0:
2014 out = p.stdout 1835 out = p.stdout
2015 if out: 1836 if out:
2016 return out[:-1].split("\0") 1837 return out[:-1].split('\0') # pylint: disable=W1401
1838 # Backslash is not anomalous
2017 return [] 1839 return []
2018 1840
2019 def DiffZ(self, name, *args): 1841 def DiffZ(self, name, *args):
@@ -2029,7 +1851,7 @@ class Project(object):
2029 out = p.process.stdout.read() 1851 out = p.process.stdout.read()
2030 r = {} 1852 r = {}
2031 if out: 1853 if out:
2032 out = iter(out[:-1].split('\0')) 1854 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
2033 while out: 1855 while out:
2034 try: 1856 try:
2035 info = out.next() 1857 info = out.next()
@@ -2165,6 +1987,9 @@ class Project(object):
2165 raise TypeError('%s() got an unexpected keyword argument %r' 1987 raise TypeError('%s() got an unexpected keyword argument %r'
2166 % (name, k)) 1988 % (name, k))
2167 if config is not None: 1989 if config is not None:
1990 if not git_require((1, 7, 2)):
1991 raise ValueError('cannot set config on command line for %s()'
1992 % name)
2168 for k, v in config.iteritems(): 1993 for k, v in config.iteritems():
2169 cmdv.append('-c') 1994 cmdv.append('-c')
2170 cmdv.append('%s=%s' % (k, v)) 1995 cmdv.append('%s=%s' % (k, v))