summaryrefslogtreecommitdiffstats
path: root/project.py
diff options
context:
space:
mode:
Diffstat (limited to 'project.py')
-rw-r--r--project.py180
1 files changed, 179 insertions, 1 deletions
diff --git a/project.py b/project.py
index 08b27710..ba7898ed 100644
--- a/project.py
+++ b/project.py
@@ -23,6 +23,7 @@ import shutil
23import stat 23import stat
24import subprocess 24import subprocess
25import sys 25import sys
26import tempfile
26import time 27import time
27 28
28from color import Coloring 29from color import Coloring
@@ -486,7 +487,30 @@ class Project(object):
486 rebase = True, 487 rebase = True,
487 groups = None, 488 groups = None,
488 sync_c = False, 489 sync_c = False,
489 upstream = None): 490 sync_s = False,
491 upstream = None,
492 parent = None,
493 is_derived = False):
494 """Init a Project object.
495
496 Args:
497 manifest: The XmlManifest object.
498 name: The `name` attribute of manifest.xml's project element.
499 remote: RemoteSpec object specifying its remote's properties.
500 gitdir: Absolute path of git directory.
501 worktree: Absolute path of git working tree.
502 relpath: Relative path of git working tree to repo's top directory.
503 revisionExpr: The `revision` attribute of manifest.xml's project element.
504 revisionId: git commit id for checking out.
505 rebase: The `rebase` attribute of manifest.xml's project element.
506 groups: The `groups` attribute of manifest.xml's project element.
507 sync_c: The `sync-c` attribute of manifest.xml's project element.
508 sync_s: The `sync-s` attribute of manifest.xml's project element.
509 upstream: The `upstream` attribute of manifest.xml's project element.
510 parent: The parent Project object.
511 is_derived: False if the project was explicitly defined in the manifest;
512 True if the project is a discovered submodule.
513 """
490 self.manifest = manifest 514 self.manifest = manifest
491 self.name = name 515 self.name = name
492 self.remote = remote 516 self.remote = remote
@@ -508,7 +532,11 @@ class Project(object):
508 self.rebase = rebase 532 self.rebase = rebase
509 self.groups = groups 533 self.groups = groups
510 self.sync_c = sync_c 534 self.sync_c = sync_c
535 self.sync_s = sync_s
511 self.upstream = upstream 536 self.upstream = upstream
537 self.parent = parent
538 self.is_derived = is_derived
539 self.subprojects = []
512 540
513 self.snapshots = {} 541 self.snapshots = {}
514 self.copyfiles = [] 542 self.copyfiles = []
@@ -529,6 +557,10 @@ class Project(object):
529 self.enabled_repo_hooks = [] 557 self.enabled_repo_hooks = []
530 558
531 @property 559 @property
560 def Derived(self):
561 return self.is_derived
562
563 @property
532 def Exists(self): 564 def Exists(self):
533 return os.path.isdir(self.gitdir) 565 return os.path.isdir(self.gitdir)
534 566
@@ -1370,6 +1402,149 @@ class Project(object):
1370 return kept 1402 return kept
1371 1403
1372 1404
1405## Submodule Management ##
1406
1407 def GetRegisteredSubprojects(self):
1408 result = []
1409 def rec(subprojects):
1410 if not subprojects:
1411 return
1412 result.extend(subprojects)
1413 for p in subprojects:
1414 rec(p.subprojects)
1415 rec(self.subprojects)
1416 return result
1417
1418 def _GetSubmodules(self):
1419 # Unfortunately we cannot call `git submodule status --recursive` here
1420 # because the working tree might not exist yet, and it cannot be used
1421 # without a working tree in its current implementation.
1422
1423 def get_submodules(gitdir, rev):
1424 # Parse .gitmodules for submodule sub_paths and sub_urls
1425 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1426 if not sub_paths:
1427 return []
1428 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1429 # revision of submodule repository
1430 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1431 submodules = []
1432 for sub_path, sub_url in zip(sub_paths, sub_urls):
1433 try:
1434 sub_rev = sub_revs[sub_path]
1435 except KeyError:
1436 # Ignore non-exist submodules
1437 continue
1438 submodules.append((sub_rev, sub_path, sub_url))
1439 return submodules
1440
1441 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1442 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
1443 def parse_gitmodules(gitdir, rev):
1444 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1445 try:
1446 p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True,
1447 bare = True, gitdir = gitdir)
1448 except GitError:
1449 return [], []
1450 if p.Wait() != 0:
1451 return [], []
1452
1453 gitmodules_lines = []
1454 fd, temp_gitmodules_path = tempfile.mkstemp()
1455 try:
1456 os.write(fd, p.stdout)
1457 os.close(fd)
1458 cmd = ['config', '--file', temp_gitmodules_path, '--list']
1459 p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True,
1460 bare = True, gitdir = gitdir)
1461 if p.Wait() != 0:
1462 return [], []
1463 gitmodules_lines = p.stdout.split('\n')
1464 except GitError:
1465 return [], []
1466 finally:
1467 os.remove(temp_gitmodules_path)
1468
1469 names = set()
1470 paths = {}
1471 urls = {}
1472 for line in gitmodules_lines:
1473 if not line:
1474 continue
1475 m = re_path.match(line)
1476 if m:
1477 names.add(m.group(1))
1478 paths[m.group(1)] = m.group(2)
1479 continue
1480 m = re_url.match(line)
1481 if m:
1482 names.add(m.group(1))
1483 urls[m.group(1)] = m.group(2)
1484 continue
1485 names = sorted(names)
1486 return ([paths.get(name, '') for name in names],
1487 [urls.get(name, '') for name in names])
1488
1489 def git_ls_tree(gitdir, rev, paths):
1490 cmd = ['ls-tree', rev, '--']
1491 cmd.extend(paths)
1492 try:
1493 p = GitCommand(None, cmd, capture_stdout = True, capture_stderr = True,
1494 bare = True, gitdir = gitdir)
1495 except GitError:
1496 return []
1497 if p.Wait() != 0:
1498 return []
1499 objects = {}
1500 for line in p.stdout.split('\n'):
1501 if not line.strip():
1502 continue
1503 object_rev, object_path = line.split()[2:4]
1504 objects[object_path] = object_rev
1505 return objects
1506
1507 try:
1508 rev = self.GetRevisionId()
1509 except GitError:
1510 return []
1511 return get_submodules(self.gitdir, rev)
1512
1513 def GetDerivedSubprojects(self):
1514 result = []
1515 if not self.Exists:
1516 # If git repo does not exist yet, querying its submodules will
1517 # mess up its states; so return here.
1518 return result
1519 for rev, path, url in self._GetSubmodules():
1520 name = self.manifest.GetSubprojectName(self, path)
1521 project = self.manifest.projects.get(name)
1522 if project:
1523 result.extend(project.GetDerivedSubprojects())
1524 continue
1525 relpath, worktree, gitdir = self.manifest.GetSubprojectPaths(self, path)
1526 remote = RemoteSpec(self.remote.name,
1527 url = url,
1528 review = self.remote.review)
1529 subproject = Project(manifest = self.manifest,
1530 name = name,
1531 remote = remote,
1532 gitdir = gitdir,
1533 worktree = worktree,
1534 relpath = relpath,
1535 revisionExpr = self.revisionExpr,
1536 revisionId = rev,
1537 rebase = self.rebase,
1538 groups = self.groups,
1539 sync_c = self.sync_c,
1540 sync_s = self.sync_s,
1541 parent = self,
1542 is_derived = True)
1543 result.append(subproject)
1544 result.extend(subproject.GetDerivedSubprojects())
1545 return result
1546
1547
1373## Direct Git Commands ## 1548## Direct Git Commands ##
1374 1549
1375 def _RemoteFetch(self, name=None, 1550 def _RemoteFetch(self, name=None,
@@ -1571,6 +1746,9 @@ class Project(object):
1571 os.remove(tmpPath) 1746 os.remove(tmpPath)
1572 if 'http_proxy' in os.environ and 'darwin' == sys.platform: 1747 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
1573 cmd += ['--proxy', os.environ['http_proxy']] 1748 cmd += ['--proxy', os.environ['http_proxy']]
1749 cookiefile = GitConfig.ForUser().GetString('http.cookiefile')
1750 if cookiefile:
1751 cmd += ['--cookie', cookiefile]
1574 cmd += [srcUrl] 1752 cmd += [srcUrl]
1575 1753
1576 if IsTrace(): 1754 if IsTrace():