diff options
-rw-r--r-- | docs/manifest-format.txt | 15 | ||||
-rw-r--r-- | manifest_xml.py | 24 | ||||
-rw-r--r-- | project.py | 57 | ||||
-rw-r--r-- | subcmds/init.py | 49 |
4 files changed, 28 insertions, 117 deletions
diff --git a/docs/manifest-format.txt b/docs/manifest-format.txt index 4b979c79..1aa93965 100644 --- a/docs/manifest-format.txt +++ b/docs/manifest-format.txt | |||
@@ -31,7 +31,7 @@ following DTD: | |||
31 | 31 | ||
32 | <!ELEMENT notice (#PCDATA)> | 32 | <!ELEMENT notice (#PCDATA)> |
33 | 33 | ||
34 | <!ELEMENT remote (projecthook?)> | 34 | <!ELEMENT remote (EMPTY)> |
35 | <!ATTLIST remote name ID #REQUIRED> | 35 | <!ATTLIST remote name ID #REQUIRED> |
36 | <!ATTLIST remote alias CDATA #IMPLIED> | 36 | <!ATTLIST remote alias CDATA #IMPLIED> |
37 | <!ATTLIST remote fetch CDATA #REQUIRED> | 37 | <!ATTLIST remote fetch CDATA #REQUIRED> |
@@ -73,10 +73,6 @@ following DTD: | |||
73 | <!ATTLIST extend-project path CDATA #IMPLIED> | 73 | <!ATTLIST extend-project path CDATA #IMPLIED> |
74 | <!ATTLIST extend-project groups CDATA #IMPLIED> | 74 | <!ATTLIST extend-project groups CDATA #IMPLIED> |
75 | 75 | ||
76 | <!ELEMENT projecthook (EMPTY)> | ||
77 | <!ATTLIST projecthook name CDATA #REQUIRED> | ||
78 | <!ATTLIST projecthook revision CDATA #REQUIRED> | ||
79 | |||
80 | <!ELEMENT remove-project (EMPTY)> | 76 | <!ELEMENT remove-project (EMPTY)> |
81 | <!ATTLIST remove-project name CDATA #REQUIRED> | 77 | <!ATTLIST remove-project name CDATA #REQUIRED> |
82 | 78 | ||
@@ -310,15 +306,6 @@ target manifest to include - it must be a usable manifest on its own. | |||
310 | Attribute `name`: the manifest to include, specified relative to | 306 | Attribute `name`: the manifest to include, specified relative to |
311 | the manifest repository's root. | 307 | the manifest repository's root. |
312 | 308 | ||
313 | Element projecthook | ||
314 | ------------------- | ||
315 | |||
316 | This element is used to define a per-remote hook git that is | ||
317 | fetched and applied to all projects using the remote. The project- | ||
318 | hook functionality allows for company/team .git/hooks to be used. | ||
319 | The hooks in the supplied project and revision are supplemented to | ||
320 | the current repo stock hooks for each project. Supplemented hooks | ||
321 | overrule any stock hooks. | ||
322 | 309 | ||
323 | Local Manifests | 310 | Local Manifests |
324 | =============== | 311 | =============== |
diff --git a/manifest_xml.py b/manifest_xml.py index 9472a08f..890c954d 100644 --- a/manifest_xml.py +++ b/manifest_xml.py | |||
@@ -64,9 +64,7 @@ class _XmlRemote(object): | |||
64 | fetch=None, | 64 | fetch=None, |
65 | manifestUrl=None, | 65 | manifestUrl=None, |
66 | review=None, | 66 | review=None, |
67 | revision=None, | 67 | revision=None): |
68 | projecthookName=None, | ||
69 | projecthookRevision=None): | ||
70 | self.name = name | 68 | self.name = name |
71 | self.fetchUrl = fetch | 69 | self.fetchUrl = fetch |
72 | self.manifestUrl = manifestUrl | 70 | self.manifestUrl = manifestUrl |
@@ -74,8 +72,6 @@ class _XmlRemote(object): | |||
74 | self.reviewUrl = review | 72 | self.reviewUrl = review |
75 | self.revision = revision | 73 | self.revision = revision |
76 | self.resolvedFetchUrl = self._resolveFetchUrl() | 74 | self.resolvedFetchUrl = self._resolveFetchUrl() |
77 | self.projecthookName = projecthookName | ||
78 | self.projecthookRevision = projecthookRevision | ||
79 | 75 | ||
80 | def __eq__(self, other): | 76 | def __eq__(self, other): |
81 | return self.__dict__ == other.__dict__ | 77 | return self.__dict__ == other.__dict__ |
@@ -171,11 +167,6 @@ class XmlManifest(object): | |||
171 | e.setAttribute('review', r.reviewUrl) | 167 | e.setAttribute('review', r.reviewUrl) |
172 | if r.revision is not None: | 168 | if r.revision is not None: |
173 | e.setAttribute('revision', r.revision) | 169 | e.setAttribute('revision', r.revision) |
174 | if r.projecthookName is not None: | ||
175 | ph = doc.createElement('projecthook') | ||
176 | ph.setAttribute('name', r.projecthookName) | ||
177 | ph.setAttribute('revision', r.projecthookRevision) | ||
178 | e.appendChild(ph) | ||
179 | 170 | ||
180 | def _ParseGroups(self, groups): | 171 | def _ParseGroups(self, groups): |
181 | return [x for x in re.split(r'[,\s]+', groups) if x] | 172 | return [x for x in re.split(r'[,\s]+', groups) if x] |
@@ -638,13 +629,7 @@ class XmlManifest(object): | |||
638 | if revision == '': | 629 | if revision == '': |
639 | revision = None | 630 | revision = None |
640 | manifestUrl = self.manifestProject.config.GetString('remote.origin.url') | 631 | manifestUrl = self.manifestProject.config.GetString('remote.origin.url') |
641 | projecthookName = None | 632 | return _XmlRemote(name, alias, fetch, manifestUrl, review, revision) |
642 | projecthookRevision = None | ||
643 | for n in node.childNodes: | ||
644 | if n.nodeName == 'projecthook': | ||
645 | projecthookName, projecthookRevision = self._ParseProjectHooks(n) | ||
646 | break | ||
647 | return _XmlRemote(name, alias, fetch, manifestUrl, review, revision, projecthookName, projecthookRevision) | ||
648 | 633 | ||
649 | def _ParseDefault(self, node): | 634 | def _ParseDefault(self, node): |
650 | """ | 635 | """ |
@@ -948,8 +933,3 @@ class XmlManifest(object): | |||
948 | diff['added'].append(toProjects[proj]) | 933 | diff['added'].append(toProjects[proj]) |
949 | 934 | ||
950 | return diff | 935 | return diff |
951 | |||
952 | def _ParseProjectHooks(self, node): | ||
953 | name = self._reqatt(node, 'name') | ||
954 | revision = self._reqatt(node, 'revision') | ||
955 | return name, revision | ||
@@ -69,6 +69,27 @@ def not_rev(r): | |||
69 | def sq(r): | 69 | def sq(r): |
70 | return "'" + r.replace("'", "'\''") + "'" | 70 | return "'" + r.replace("'", "'\''") + "'" |
71 | 71 | ||
72 | _project_hook_list = None | ||
73 | def _ProjectHooks(): | ||
74 | """List the hooks present in the 'hooks' directory. | ||
75 | |||
76 | These hooks are project hooks and are copied to the '.git/hooks' directory | ||
77 | of all subprojects. | ||
78 | |||
79 | This function caches the list of hooks (based on the contents of the | ||
80 | 'repo/hooks' directory) on the first call. | ||
81 | |||
82 | Returns: | ||
83 | A list of absolute paths to all of the files in the hooks directory. | ||
84 | """ | ||
85 | global _project_hook_list | ||
86 | if _project_hook_list is None: | ||
87 | d = os.path.realpath(os.path.abspath(os.path.dirname(__file__))) | ||
88 | d = os.path.join(d, 'hooks') | ||
89 | _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)] | ||
90 | return _project_hook_list | ||
91 | |||
92 | |||
72 | class DownloadedChange(object): | 93 | class DownloadedChange(object): |
73 | _commit_cache = None | 94 | _commit_cache = None |
74 | 95 | ||
@@ -2085,7 +2106,7 @@ class Project(object): | |||
2085 | if GitCommand(self, cmd).Wait() != 0: | 2106 | if GitCommand(self, cmd).Wait() != 0: |
2086 | raise GitError('%s merge %s ' % (self.name, head)) | 2107 | raise GitError('%s merge %s ' % (self.name, head)) |
2087 | 2108 | ||
2088 | def _InitGitDir(self, mirror_git=None, MirrorOverride=False): | 2109 | def _InitGitDir(self, mirror_git=None): |
2089 | if not os.path.exists(self.gitdir): | 2110 | if not os.path.exists(self.gitdir): |
2090 | 2111 | ||
2091 | # Initialize the bare repository, which contains all of the objects. | 2112 | # Initialize the bare repository, which contains all of the objects. |
@@ -2127,38 +2148,11 @@ class Project(object): | |||
2127 | for key in ['user.name', 'user.email']: | 2148 | for key in ['user.name', 'user.email']: |
2128 | if m.Has(key, include_defaults=False): | 2149 | if m.Has(key, include_defaults=False): |
2129 | self.config.SetString(key, m.GetString(key)) | 2150 | self.config.SetString(key, m.GetString(key)) |
2130 | if self.manifest.IsMirror and not MirrorOverride: | 2151 | if self.manifest.IsMirror: |
2131 | self.config.SetString('core.bare', 'true') | 2152 | self.config.SetString('core.bare', 'true') |
2132 | else: | 2153 | else: |
2133 | self.config.SetString('core.bare', None) | 2154 | self.config.SetString('core.bare', None) |
2134 | 2155 | ||
2135 | def _ProjectHooks(self, remote, repodir): | ||
2136 | """List the hooks present in the 'hooks' directory. | ||
2137 | |||
2138 | These hooks are project hooks and are copied to the '.git/hooks' directory | ||
2139 | of all subprojects. | ||
2140 | |||
2141 | The remote projecthooks supplement/overrule any stockhook making it possible to | ||
2142 | have a combination of hooks both from the remote projecthook and | ||
2143 | .repo/hooks directories. | ||
2144 | |||
2145 | Returns: | ||
2146 | A list of absolute paths to all of the files in the hooks directory and | ||
2147 | projecthooks files, excluding the .git folder. | ||
2148 | """ | ||
2149 | hooks = {} | ||
2150 | d = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'hooks') | ||
2151 | hooks = dict([(x, os.path.join(d, x)) for x in os.listdir(d)]) | ||
2152 | if remote is not None: | ||
2153 | if remote.projecthookName is not None: | ||
2154 | d = os.path.abspath('%s/projecthooks/%s/%s' % (repodir, remote.name, remote.projecthookName)) | ||
2155 | if os.path.isdir(d): | ||
2156 | hooks.update(dict([(x, os.path.join(d, x)) for x in os.listdir(d)])) | ||
2157 | |||
2158 | if hooks.has_key('.git'): | ||
2159 | del hooks['.git'] | ||
2160 | return hooks.values() | ||
2161 | |||
2162 | def _UpdateHooks(self): | 2156 | def _UpdateHooks(self): |
2163 | if os.path.exists(self.gitdir): | 2157 | if os.path.exists(self.gitdir): |
2164 | self._InitHooks() | 2158 | self._InitHooks() |
@@ -2167,10 +2161,7 @@ class Project(object): | |||
2167 | hooks = os.path.realpath(self._gitdir_path('hooks')) | 2161 | hooks = os.path.realpath(self._gitdir_path('hooks')) |
2168 | if not os.path.exists(hooks): | 2162 | if not os.path.exists(hooks): |
2169 | os.makedirs(hooks) | 2163 | os.makedirs(hooks) |
2170 | pr = None | 2164 | for stock_hook in _ProjectHooks(): |
2171 | if self is not self.manifest.manifestProject: | ||
2172 | pr = self.manifest.remotes.get(self.remote.name) | ||
2173 | for stock_hook in self._ProjectHooks(pr, self.manifest.repodir): | ||
2174 | name = os.path.basename(stock_hook) | 2165 | name = os.path.basename(stock_hook) |
2175 | 2166 | ||
2176 | if name in ('commit-msg',) and not self.remote.review \ | 2167 | if name in ('commit-msg',) and not self.remote.review \ |
diff --git a/subcmds/init.py b/subcmds/init.py index c5bf2823..b73de71c 100644 --- a/subcmds/init.py +++ b/subcmds/init.py | |||
@@ -32,7 +32,7 @@ else: | |||
32 | from color import Coloring | 32 | from color import Coloring |
33 | from command import InteractiveCommand, MirrorSafeCommand | 33 | from command import InteractiveCommand, MirrorSafeCommand |
34 | from error import ManifestParseError | 34 | from error import ManifestParseError |
35 | from project import SyncBuffer, MetaProject | 35 | from project import SyncBuffer |
36 | from git_config import GitConfig | 36 | from git_config import GitConfig |
37 | from git_command import git_require, MIN_GIT_VERSION | 37 | from git_command import git_require, MIN_GIT_VERSION |
38 | 38 | ||
@@ -374,52 +374,6 @@ to update the working directory files. | |||
374 | print(' rm -r %s/.repo' % self.manifest.topdir) | 374 | print(' rm -r %s/.repo' % self.manifest.topdir) |
375 | print('and try again.') | 375 | print('and try again.') |
376 | 376 | ||
377 | def _SyncProjectHooks(self, opt, repodir): | ||
378 | """Downloads the defined hooks supplied in the projecthooks element | ||
379 | |||
380 | """ | ||
381 | # Always delete projecthooks and re-download for every new init. | ||
382 | projecthooksdir = os.path.join(repodir, 'projecthooks') | ||
383 | if os.path.exists(projecthooksdir): | ||
384 | shutil.rmtree(projecthooksdir) | ||
385 | for remotename in self.manifest.remotes: | ||
386 | r = self.manifest.remotes.get(remotename) | ||
387 | if r.projecthookName is not None and r.projecthookRevision is not None: | ||
388 | projecthookurl = r.resolvedFetchUrl.rstrip('/') + '/' + r.projecthookName | ||
389 | |||
390 | ph = MetaProject(manifest = self.manifest, | ||
391 | name = r.projecthookName, | ||
392 | gitdir = os.path.join(projecthooksdir,'%s/%s.git' % (remotename, r.projecthookName)), | ||
393 | worktree = os.path.join(projecthooksdir,'%s/%s' % (remotename, r.projecthookName))) | ||
394 | |||
395 | ph.revisionExpr = r.projecthookRevision | ||
396 | is_new = not ph.Exists | ||
397 | |||
398 | if is_new: | ||
399 | if not opt.quiet: | ||
400 | print('Get projecthook %s' % \ | ||
401 | GitConfig.ForUser().UrlInsteadOf(projecthookurl), file=sys.stderr) | ||
402 | ph._InitGitDir(MirrorOverride=True) | ||
403 | |||
404 | phr = ph.GetRemote(remotename) | ||
405 | phr.name = 'origin' | ||
406 | phr.url = projecthookurl | ||
407 | phr.ResetFetch() | ||
408 | phr.Save() | ||
409 | |||
410 | if not ph.Sync_NetworkHalf(quiet=opt.quiet, is_new=is_new, clone_bundle=False): | ||
411 | print('fatal: cannot obtain projecthook %s' % phr.url, file=sys.stderr) | ||
412 | |||
413 | # Better delete the git dir if we created it; otherwise next | ||
414 | # time (when user fixes problems) we won't go through the "is_new" logic. | ||
415 | if is_new: | ||
416 | shutil.rmtree(ph.gitdir) | ||
417 | sys.exit(1) | ||
418 | |||
419 | syncbuf = SyncBuffer(ph.config) | ||
420 | ph.Sync_LocalHalf(syncbuf) | ||
421 | syncbuf.Finish() | ||
422 | |||
423 | def Execute(self, opt, args): | 377 | def Execute(self, opt, args): |
424 | git_require(MIN_GIT_VERSION, fail=True) | 378 | git_require(MIN_GIT_VERSION, fail=True) |
425 | 379 | ||
@@ -435,7 +389,6 @@ to update the working directory files. | |||
435 | 389 | ||
436 | self._SyncManifest(opt) | 390 | self._SyncManifest(opt) |
437 | self._LinkManifest(opt.manifest_name) | 391 | self._LinkManifest(opt.manifest_name) |
438 | self._SyncProjectHooks(opt, self.manifest.repodir) | ||
439 | 392 | ||
440 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: | 393 | if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror: |
441 | if opt.config_name or self._ShouldConfigureUser(): | 394 | if opt.config_name or self._ShouldConfigureUser(): |