summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--command.py19
-rw-r--r--gitc_utils.py37
-rwxr-xr-xmain.py9
-rw-r--r--manifest_xml.py36
-rw-r--r--project.py18
-rw-r--r--subcmds/start.py42
-rw-r--r--subcmds/sync.py44
7 files changed, 166 insertions, 39 deletions
diff --git a/command.py b/command.py
index 38cacd3b..78dab96d 100644
--- a/command.py
+++ b/command.py
@@ -106,13 +106,13 @@ class Command(object):
106 def _UpdatePathToProjectMap(self, project): 106 def _UpdatePathToProjectMap(self, project):
107 self._by_path[project.worktree] = project 107 self._by_path[project.worktree] = project
108 108
109 def _GetProjectByPath(self, path): 109 def _GetProjectByPath(self, manifest, path):
110 project = None 110 project = None
111 if os.path.exists(path): 111 if os.path.exists(path):
112 oldpath = None 112 oldpath = None
113 while path \ 113 while path \
114 and path != oldpath \ 114 and path != oldpath \
115 and path != self.manifest.topdir: 115 and path != manifest.topdir:
116 try: 116 try:
117 project = self._by_path[path] 117 project = self._by_path[path]
118 break 118 break
@@ -126,13 +126,16 @@ class Command(object):
126 pass 126 pass
127 return project 127 return project
128 128
129 def GetProjects(self, args, groups='', missing_ok=False, submodules_ok=False): 129 def GetProjects(self, args, manifest=None, groups='', missing_ok=False,
130 submodules_ok=False):
130 """A list of projects that match the arguments. 131 """A list of projects that match the arguments.
131 """ 132 """
132 all_projects_list = self.manifest.projects 133 if not manifest:
134 manifest = self.manifest
135 all_projects_list = manifest.projects
133 result = [] 136 result = []
134 137
135 mp = self.manifest.manifestProject 138 mp = manifest.manifestProject
136 139
137 if not groups: 140 if not groups:
138 groups = mp.config.GetString('manifest.groups') 141 groups = mp.config.GetString('manifest.groups')
@@ -155,11 +158,11 @@ class Command(object):
155 self._ResetPathToProjectMap(all_projects_list) 158 self._ResetPathToProjectMap(all_projects_list)
156 159
157 for arg in args: 160 for arg in args:
158 projects = self.manifest.GetProjectsWithName(arg) 161 projects = manifest.GetProjectsWithName(arg)
159 162
160 if not projects: 163 if not projects:
161 path = os.path.abspath(arg).replace('\\', '/') 164 path = os.path.abspath(arg).replace('\\', '/')
162 project = self._GetProjectByPath(path) 165 project = self._GetProjectByPath(manifest, path)
163 166
164 # If it's not a derived project, update path->project mapping and 167 # If it's not a derived project, update path->project mapping and
165 # search again, as arg might actually point to a derived subproject. 168 # search again, as arg might actually point to a derived subproject.
@@ -170,7 +173,7 @@ class Command(object):
170 self._UpdatePathToProjectMap(subproject) 173 self._UpdatePathToProjectMap(subproject)
171 search_again = True 174 search_again = True
172 if search_again: 175 if search_again:
173 project = self._GetProjectByPath(path) or project 176 project = self._GetProjectByPath(manifest, path) or project
174 177
175 if project: 178 if project:
176 projects = [project] 179 projects = [project]
diff --git a/gitc_utils.py b/gitc_utils.py
index ef028b08..4d8d5366 100644
--- a/gitc_utils.py
+++ b/gitc_utils.py
@@ -16,6 +16,7 @@
16from __future__ import print_function 16from __future__ import print_function
17import os 17import os
18import sys 18import sys
19import time
19 20
20import git_command 21import git_command
21import git_config 22import git_config
@@ -26,6 +27,18 @@ GITC_MANIFEST_DIR = '/usr/local/google/gitc/'
26GITC_FS_ROOT_DIR = '/gitc/manifest-rw/' 27GITC_FS_ROOT_DIR = '/gitc/manifest-rw/'
27NUM_BATCH_RETRIEVE_REVISIONID = 300 28NUM_BATCH_RETRIEVE_REVISIONID = 300
28 29
30def parse_clientdir(gitc_fs_path):
31 """Parse a path in the GITC FS and return its client name.
32
33 @param gitc_fs_path: A subdirectory path within the GITC_FS_ROOT_DIR.
34
35 @returns: The GITC client name
36 """
37 if (gitc_fs_path == GITC_FS_ROOT_DIR or
38 not gitc_fs_path.startswith(GITC_FS_ROOT_DIR)):
39 return None
40 return gitc_fs_path.split(GITC_FS_ROOT_DIR)[1].split('/')[0]
41
29def _set_project_revisions(projects): 42def _set_project_revisions(projects):
30 """Sets the revisionExpr for a list of projects. 43 """Sets the revisionExpr for a list of projects.
31 44
@@ -50,19 +63,37 @@ def _set_project_revisions(projects):
50 sys.exit(1) 63 sys.exit(1)
51 proj.revisionExpr = gitcmd.stdout.split('\t')[0] 64 proj.revisionExpr = gitcmd.stdout.split('\t')[0]
52 65
53def generate_gitc_manifest(client_dir, manifest): 66def generate_gitc_manifest(client_dir, manifest, projects=None):
54 """Generate a manifest for shafsd to use for this GITC client. 67 """Generate a manifest for shafsd to use for this GITC client.
55 68
56 @param client_dir: GITC client directory to install the .manifest file in. 69 @param client_dir: GITC client directory to install the .manifest file in.
57 @param manifest: XmlManifest object representing the repo manifest. 70 @param manifest: XmlManifest object representing the repo manifest.
71 @param projects: List of projects we want to update, this must be a sublist
72 of manifest.projects to work properly. If not provided,
73 manifest.projects is used.
58 """ 74 """
59 print('Generating GITC Manifest by fetching revision SHAs for each ' 75 print('Generating GITC Manifest by fetching revision SHAs for each '
60 'project.') 76 'project.')
77 if projects is None:
78 projects = manifest.projects
61 index = 0 79 index = 0
62 while index < len(manifest.projects): 80 while index < len(projects):
63 _set_project_revisions( 81 _set_project_revisions(
64 manifest.projects[index:(index+NUM_BATCH_RETRIEVE_REVISIONID)]) 82 projects[index:(index+NUM_BATCH_RETRIEVE_REVISIONID)])
65 index += NUM_BATCH_RETRIEVE_REVISIONID 83 index += NUM_BATCH_RETRIEVE_REVISIONID
66 # Save the manifest. 84 # Save the manifest.
85 save_manifest(manifest, client_dir=client_dir)
86
87def save_manifest(manifest, client_dir=None):
88 """Save the manifest file in the client_dir.
89
90 @param client_dir: Client directory to save the manifest in.
91 @param manifest: Manifest object to save.
92 """
93 if not client_dir:
94 client_dir = manifest.gitc_client_dir
67 with open(os.path.join(client_dir, '.manifest'), 'w') as f: 95 with open(os.path.join(client_dir, '.manifest'), 'w') as f:
68 manifest.Save(f) 96 manifest.Save(f)
97 # TODO(sbasi/jorg): Come up with a solution to remove the sleep below.
98 # Give the GITC filesystem time to register the manifest changes.
99 time.sleep(3) \ No newline at end of file
diff --git a/main.py b/main.py
index 6736abc9..adfaffb0 100755
--- a/main.py
+++ b/main.py
@@ -51,7 +51,8 @@ from error import ManifestParseError
51from error import NoManifestException 51from error import NoManifestException
52from error import NoSuchProjectError 52from error import NoSuchProjectError
53from error import RepoChangedException 53from error import RepoChangedException
54from manifest_xml import XmlManifest 54import gitc_utils
55from manifest_xml import GitcManifest, XmlManifest
55from pager import RunPager 56from pager import RunPager
56from wrapper import WrapperPath, Wrapper 57from wrapper import WrapperPath, Wrapper
57 58
@@ -129,6 +130,12 @@ class _Repo(object):
129 130
130 cmd.repodir = self.repodir 131 cmd.repodir = self.repodir
131 cmd.manifest = XmlManifest(cmd.repodir) 132 cmd.manifest = XmlManifest(cmd.repodir)
133 cmd.gitc_manifest = None
134 gitc_client_name = gitc_utils.parse_clientdir(os.getcwd())
135 if gitc_client_name:
136 cmd.gitc_manifest = GitcManifest(cmd.repodir, gitc_client_name)
137 cmd.manifest.isGitcClient = True
138
132 Editor.globalConfig = cmd.manifest.globalConfig 139 Editor.globalConfig = cmd.manifest.globalConfig
133 140
134 if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror: 141 if not isinstance(cmd, MirrorSafeCommand) and cmd.manifest.IsMirror:
diff --git a/manifest_xml.py b/manifest_xml.py
index 6dc01a47..b33ec627 100644
--- a/manifest_xml.py
+++ b/manifest_xml.py
@@ -29,6 +29,7 @@ else:
29 urllib = imp.new_module('urllib') 29 urllib = imp.new_module('urllib')
30 urllib.parse = urlparse 30 urllib.parse = urlparse
31 31
32import gitc_utils
32from git_config import GitConfig 33from git_config import GitConfig
33from git_refs import R_HEADS, HEAD 34from git_refs import R_HEADS, HEAD
34from project import RemoteSpec, Project, MetaProject 35from project import RemoteSpec, Project, MetaProject
@@ -112,6 +113,7 @@ class XmlManifest(object):
112 self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) 113 self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME)
113 self.globalConfig = GitConfig.ForUser() 114 self.globalConfig = GitConfig.ForUser()
114 self.localManifestWarning = False 115 self.localManifestWarning = False
116 self.isGitcClient = False
115 117
116 self.repoProject = MetaProject(self, 'repo', 118 self.repoProject = MetaProject(self, 'repo',
117 gitdir = os.path.join(repodir, 'repo/.git'), 119 gitdir = os.path.join(repodir, 'repo/.git'),
@@ -306,6 +308,8 @@ class XmlManifest(object):
306 if p.clone_depth: 308 if p.clone_depth:
307 e.setAttribute('clone-depth', str(p.clone_depth)) 309 e.setAttribute('clone-depth', str(p.clone_depth))
308 310
311 self._output_manifest_project_extras(p, e)
312
309 if p.subprojects: 313 if p.subprojects:
310 subprojects = set(subp.name for subp in p.subprojects) 314 subprojects = set(subp.name for subp in p.subprojects)
311 output_projects(p, e, list(sorted(subprojects))) 315 output_projects(p, e, list(sorted(subprojects)))
@@ -323,6 +327,10 @@ class XmlManifest(object):
323 327
324 doc.writexml(fd, '', ' ', '\n', 'UTF-8') 328 doc.writexml(fd, '', ' ', '\n', 'UTF-8')
325 329
330 def _output_manifest_project_extras(self, p, e):
331 """Manifests can modify e if they support extra project attributes."""
332 pass
333
326 @property 334 @property
327 def paths(self): 335 def paths(self):
328 self._Load() 336 self._Load()
@@ -712,7 +720,7 @@ class XmlManifest(object):
712 def _UnjoinName(self, parent_name, name): 720 def _UnjoinName(self, parent_name, name):
713 return os.path.relpath(name, parent_name) 721 return os.path.relpath(name, parent_name)
714 722
715 def _ParseProject(self, node, parent = None): 723 def _ParseProject(self, node, parent = None, **extra_proj_attrs):
716 """ 724 """
717 reads a <project> element from the manifest file 725 reads a <project> element from the manifest file
718 """ 726 """
@@ -807,7 +815,8 @@ class XmlManifest(object):
807 clone_depth = clone_depth, 815 clone_depth = clone_depth,
808 upstream = upstream, 816 upstream = upstream,
809 parent = parent, 817 parent = parent,
810 dest_branch = dest_branch) 818 dest_branch = dest_branch,
819 **extra_proj_attrs)
811 820
812 for n in node.childNodes: 821 for n in node.childNodes:
813 if n.nodeName == 'copyfile': 822 if n.nodeName == 'copyfile':
@@ -938,3 +947,26 @@ class XmlManifest(object):
938 diff['added'].append(toProjects[proj]) 947 diff['added'].append(toProjects[proj])
939 948
940 return diff 949 return diff
950
951
952class GitcManifest(XmlManifest):
953
954 def __init__(self, repodir, gitc_client_name):
955 """Initialize the GitcManifest object."""
956 super(GitcManifest, self).__init__(repodir)
957 self.isGitcClient = True
958 self.gitc_client_name = gitc_client_name
959 self.gitc_client_dir = os.path.join(gitc_utils.GITC_MANIFEST_DIR,
960 gitc_client_name)
961 self.manifestFile = os.path.join(self.gitc_client_dir, '.manifest')
962
963 def _ParseProject(self, node, parent = None):
964 """Override _ParseProject and add support for GITC specific attributes."""
965 return super(GitcManifest, self)._ParseProject(
966 node, parent=parent, old_revision=node.getAttribute('old-revision'))
967
968 def _output_manifest_project_extras(self, p, e):
969 """Output GITC Specific Project attributes"""
970 if p.old_revision:
971 e.setAttribute('old-revision', str(p.old_revision))
972
diff --git a/project.py b/project.py
index a8d012d2..f964b2fc 100644
--- a/project.py
+++ b/project.py
@@ -572,7 +572,8 @@ class Project(object):
572 parent=None, 572 parent=None,
573 is_derived=False, 573 is_derived=False,
574 dest_branch=None, 574 dest_branch=None,
575 optimized_fetch=False): 575 optimized_fetch=False,
576 old_revision=None):
576 """Init a Project object. 577 """Init a Project object.
577 578
578 Args: 579 Args:
@@ -596,6 +597,7 @@ class Project(object):
596 dest_branch: The branch to which to push changes for review by default. 597 dest_branch: The branch to which to push changes for review by default.
597 optimized_fetch: If True, when a project is set to a sha1 revision, only 598 optimized_fetch: If True, when a project is set to a sha1 revision, only
598 fetch from the remote if the sha1 is not present locally. 599 fetch from the remote if the sha1 is not present locally.
600 old_revision: saved git commit id for open GITC projects.
599 """ 601 """
600 self.manifest = manifest 602 self.manifest = manifest
601 self.name = name 603 self.name = name
@@ -643,6 +645,7 @@ class Project(object):
643 self.bare_ref = GitRefs(gitdir) 645 self.bare_ref = GitRefs(gitdir)
644 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir) 646 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
645 self.dest_branch = dest_branch 647 self.dest_branch = dest_branch
648 self.old_revision = old_revision
646 649
647 # This will be filled in if a project is later identified to be the 650 # This will be filled in if a project is later identified to be the
648 # project containing repo hooks. 651 # project containing repo hooks.
@@ -1195,6 +1198,8 @@ class Project(object):
1195 self._InitHooks() 1198 self._InitHooks()
1196 1199
1197 def _CopyAndLinkFiles(self): 1200 def _CopyAndLinkFiles(self):
1201 if self.manifest.isGitcClient:
1202 return
1198 for copyfile in self.copyfiles: 1203 for copyfile in self.copyfiles:
1199 copyfile._Copy() 1204 copyfile._Copy()
1200 for linkfile in self.linkfiles: 1205 for linkfile in self.linkfiles:
@@ -1425,9 +1430,11 @@ class Project(object):
1425 1430
1426## Branch Management ## 1431## Branch Management ##
1427 1432
1428 def StartBranch(self, name): 1433 def StartBranch(self, name, branch_merge=''):
1429 """Create a new branch off the manifest's revision. 1434 """Create a new branch off the manifest's revision.
1430 """ 1435 """
1436 if not branch_merge:
1437 branch_merge = self.revisionExpr
1431 head = self.work_git.GetHead() 1438 head = self.work_git.GetHead()
1432 if head == (R_HEADS + name): 1439 if head == (R_HEADS + name):
1433 return True 1440 return True
@@ -1441,9 +1448,9 @@ class Project(object):
1441 1448
1442 branch = self.GetBranch(name) 1449 branch = self.GetBranch(name)
1443 branch.remote = self.GetRemote(self.remote.name) 1450 branch.remote = self.GetRemote(self.remote.name)
1444 branch.merge = self.revisionExpr 1451 branch.merge = branch_merge
1445 if not branch.merge.startswith('refs/') and not ID_RE.match(self.revisionExpr): 1452 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1446 branch.merge = R_HEADS + self.revisionExpr 1453 branch.merge = R_HEADS + branch_merge
1447 revid = self.GetRevisionId(all_refs) 1454 revid = self.GetRevisionId(all_refs)
1448 1455
1449 if head.startswith(R_HEADS): 1456 if head.startswith(R_HEADS):
@@ -1451,7 +1458,6 @@ class Project(object):
1451 head = all_refs[head] 1458 head = all_refs[head]
1452 except KeyError: 1459 except KeyError:
1453 head = None 1460 head = None
1454
1455 if revid and head and revid == head: 1461 if revid and head and revid == head:
1456 ref = os.path.join(self.gitdir, R_HEADS + name) 1462 ref = os.path.join(self.gitdir, R_HEADS + name)
1457 try: 1463 try:
diff --git a/subcmds/start.py b/subcmds/start.py
index 60ad41e0..188fd7c6 100644
--- a/subcmds/start.py
+++ b/subcmds/start.py
@@ -14,11 +14,15 @@
14# limitations under the License. 14# limitations under the License.
15 15
16from __future__ import print_function 16from __future__ import print_function
17import os
17import sys 18import sys
19
18from command import Command 20from command import Command
19from git_config import IsId 21from git_config import IsId
20from git_command import git 22from git_command import git
23import gitc_utils
21from progress import Progress 24from progress import Progress
25from project import SyncBuffer
22 26
23class Start(Command): 27class Start(Command):
24 common = True 28 common = True
@@ -53,20 +57,50 @@ revision specified in the manifest.
53 print("error: at least one project must be specified", file=sys.stderr) 57 print("error: at least one project must be specified", file=sys.stderr)
54 sys.exit(1) 58 sys.exit(1)
55 59
56 all_projects = self.GetProjects(projects) 60 proj_name_to_gitc_proj_dict = {}
61 if self.gitc_manifest:
62 all_projects = self.GetProjects(projects, manifest=self.gitc_manifest,
63 missing_ok=True)
64 for project in all_projects:
65 if project.old_revision:
66 project.already_synced = True
67 else:
68 project.already_synced = False
69 project.old_revision = project.revisionExpr
70 proj_name_to_gitc_proj_dict[project.name] = project
71 project.revisionExpr = None
72 # Save the GITC manifest.
73 gitc_utils.save_manifest(self.gitc_manifest)
57 74
75 all_projects = self.GetProjects(projects,
76 missing_ok=bool(self.gitc_manifest))
58 pm = Progress('Starting %s' % nb, len(all_projects)) 77 pm = Progress('Starting %s' % nb, len(all_projects))
59 for project in all_projects: 78 for project in all_projects:
60 pm.update() 79 pm.update()
80 if self.gitc_manifest:
81 gitc_project = proj_name_to_gitc_proj_dict[project.name]
82 # Sync projects that have already been opened.
83 if not gitc_project.already_synced:
84 proj_localdir = os.path.join(self.gitc_manifest.gitc_client_dir,
85 project.relpath)
86 project.worktree = proj_localdir
87 if not os.path.exists(proj_localdir):
88 os.makedirs(proj_localdir)
89 project.Sync_NetworkHalf()
90 sync_buf = SyncBuffer(self.manifest.manifestProject.config)
91 project.Sync_LocalHalf(sync_buf)
92 project.revisionExpr = gitc_project.old_revision
93
61 # If the current revision is a specific SHA1 then we can't push back 94 # If the current revision is a specific SHA1 then we can't push back
62 # to it; so substitute with dest_branch if defined, or with manifest 95 # to it; so substitute with dest_branch if defined, or with manifest
63 # default revision instead. 96 # default revision instead.
97 branch_merge = ''
64 if IsId(project.revisionExpr): 98 if IsId(project.revisionExpr):
65 if project.dest_branch: 99 if project.dest_branch:
66 project.revisionExpr = project.dest_branch 100 branch_merge = project.dest_branch
67 else: 101 else:
68 project.revisionExpr = self.manifest.default.revisionExpr 102 branch_merge = self.manifest.default.revisionExpr
69 if not project.StartBranch(nb): 103 if not project.StartBranch(nb, branch_merge=branch_merge):
70 err.append(project) 104 err.append(project)
71 pm.end() 105 pm.end()
72 106
diff --git a/subcmds/sync.py b/subcmds/sync.py
index ad0ecdf4..934aaa80 100644
--- a/subcmds/sync.py
+++ b/subcmds/sync.py
@@ -549,15 +549,6 @@ later is required to fix a server side protocol bug.
549 cwd.split(gitc_utils.GITC_MANIFEST_DIR)[1])) 549 cwd.split(gitc_utils.GITC_MANIFEST_DIR)[1]))
550 sys.exit(1) 550 sys.exit(1)
551 551
552 self._gitc_sync = False
553 if cwd.startswith(gitc_utils.GITC_FS_ROOT_DIR):
554 self._gitc_sync = True
555 self._client_name = cwd.split(gitc_utils.GITC_FS_ROOT_DIR)[1].split(
556 '/')[0]
557 self._client_dir = os.path.join(gitc_utils.GITC_MANIFEST_DIR,
558 self._client_name)
559 print('Updating GITC client: %s' % self._client_name)
560
561 if opt.manifest_name: 552 if opt.manifest_name:
562 self.manifest.Override(opt.manifest_name) 553 self.manifest.Override(opt.manifest_name)
563 554
@@ -677,12 +668,6 @@ later is required to fix a server side protocol bug.
677 if opt.repo_upgraded: 668 if opt.repo_upgraded:
678 _PostRepoUpgrade(self.manifest, quiet=opt.quiet) 669 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
679 670
680 if self._gitc_sync:
681 gitc_utils.generate_gitc_manifest(self._client_dir, self.manifest)
682 print('GITC client successfully synced.')
683 return
684
685
686 if not opt.local_only: 671 if not opt.local_only:
687 mp.Sync_NetworkHalf(quiet=opt.quiet, 672 mp.Sync_NetworkHalf(quiet=opt.quiet,
688 current_branch_only=opt.current_branch_only, 673 current_branch_only=opt.current_branch_only,
@@ -697,6 +682,35 @@ later is required to fix a server side protocol bug.
697 self._ReloadManifest(manifest_name) 682 self._ReloadManifest(manifest_name)
698 if opt.jobs is None: 683 if opt.jobs is None:
699 self.jobs = self.manifest.default.sync_j 684 self.jobs = self.manifest.default.sync_j
685
686 # TODO (sbasi) - Add support for manifest changes, aka projects
687 # have been added or deleted from the manifest.
688 if self.gitc_manifest:
689 gitc_manifest_projects = self.GetProjects(args,
690 manifest=self.gitc_manifest,
691 missing_ok=True)
692 gitc_projects = []
693 opened_projects = []
694 for project in gitc_manifest_projects:
695 if not project.old_revision:
696 gitc_projects.append(project)
697 else:
698 opened_projects.append(project)
699
700 if gitc_projects and not opt.local_only:
701 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
702 gitc_utils.generate_gitc_manifest(self.gitc_manifest.gitc_client_dir,
703 self.gitc_manifest,
704 gitc_projects)
705 print('GITC client successfully synced.')
706
707 # The opened projects need to be synced as normal, therefore we
708 # generate a new args list to represent the opened projects.
709 args = []
710 for proj in opened_projects:
711 args.append(os.path.relpath(proj.worktree, cwd))
712 if not args:
713 return
700 all_projects = self.GetProjects(args, 714 all_projects = self.GetProjects(args,
701 missing_ok=True, 715 missing_ok=True,
702 submodules_ok=opt.fetch_submodules) 716 submodules_ok=opt.fetch_submodules)