mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-21 07:16:21 +00:00
Merge "Implementation of manifest defined githooks"
This commit is contained in:
commit
85e8267031
@ -31,7 +31,7 @@ following DTD:
|
||||
|
||||
<!ELEMENT notice (#PCDATA)>
|
||||
|
||||
<!ELEMENT remote (EMPTY)>
|
||||
<!ELEMENT remote (projecthook?)>
|
||||
<!ATTLIST remote name ID #REQUIRED>
|
||||
<!ATTLIST remote alias CDATA #IMPLIED>
|
||||
<!ATTLIST remote fetch CDATA #REQUIRED>
|
||||
@ -73,6 +73,10 @@ following DTD:
|
||||
<!ATTLIST extend-project path CDATA #IMPLIED>
|
||||
<!ATTLIST extend-project groups CDATA #IMPLIED>
|
||||
|
||||
<!ELEMENT projecthook (EMPTY)>
|
||||
<!ATTLIST projecthook name CDATA #REQUIRED>
|
||||
<!ATTLIST projecthook revision CDATA #REQUIRED>
|
||||
|
||||
<!ELEMENT remove-project (EMPTY)>
|
||||
<!ATTLIST remove-project name CDATA #REQUIRED>
|
||||
|
||||
@ -306,6 +310,15 @@ target manifest to include - it must be a usable manifest on its own.
|
||||
Attribute `name`: the manifest to include, specified relative to
|
||||
the manifest repository's root.
|
||||
|
||||
Element projecthook
|
||||
-------------------
|
||||
|
||||
This element is used to define a per-remote hook git that is
|
||||
fetched and applied to all projects using the remote. The project-
|
||||
hook functionality allows for company/team .git/hooks to be used.
|
||||
The hooks in the supplied project and revision are supplemented to
|
||||
the current repo stock hooks for each project. Supplemented hooks
|
||||
overrule any stock hooks.
|
||||
|
||||
Local Manifests
|
||||
===============
|
||||
|
@ -64,7 +64,9 @@ class _XmlRemote(object):
|
||||
fetch=None,
|
||||
manifestUrl=None,
|
||||
review=None,
|
||||
revision=None):
|
||||
revision=None,
|
||||
projecthookName=None,
|
||||
projecthookRevision=None):
|
||||
self.name = name
|
||||
self.fetchUrl = fetch
|
||||
self.manifestUrl = manifestUrl
|
||||
@ -72,6 +74,8 @@ class _XmlRemote(object):
|
||||
self.reviewUrl = review
|
||||
self.revision = revision
|
||||
self.resolvedFetchUrl = self._resolveFetchUrl()
|
||||
self.projecthookName = projecthookName
|
||||
self.projecthookRevision = projecthookRevision
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
@ -167,6 +171,11 @@ class XmlManifest(object):
|
||||
e.setAttribute('review', r.reviewUrl)
|
||||
if r.revision is not None:
|
||||
e.setAttribute('revision', r.revision)
|
||||
if r.projecthookName is not None:
|
||||
ph = doc.createElement('projecthook')
|
||||
ph.setAttribute('name', r.projecthookName)
|
||||
ph.setAttribute('revision', r.projecthookRevision)
|
||||
e.appendChild(ph)
|
||||
|
||||
def _ParseGroups(self, groups):
|
||||
return [x for x in re.split(r'[,\s]+', groups) if x]
|
||||
@ -629,7 +638,13 @@ class XmlManifest(object):
|
||||
if revision == '':
|
||||
revision = None
|
||||
manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
|
||||
return _XmlRemote(name, alias, fetch, manifestUrl, review, revision)
|
||||
projecthookName = None
|
||||
projecthookRevision = None
|
||||
for n in node.childNodes:
|
||||
if n.nodeName == 'projecthook':
|
||||
projecthookName, projecthookRevision = self._ParseProjectHooks(n)
|
||||
break
|
||||
return _XmlRemote(name, alias, fetch, manifestUrl, review, revision, projecthookName, projecthookRevision)
|
||||
|
||||
def _ParseDefault(self, node):
|
||||
"""
|
||||
@ -933,3 +948,8 @@ class XmlManifest(object):
|
||||
diff['added'].append(toProjects[proj])
|
||||
|
||||
return diff
|
||||
|
||||
def _ParseProjectHooks(self, node):
|
||||
name = self._reqatt(node, 'name')
|
||||
revision = self._reqatt(node, 'revision')
|
||||
return name, revision
|
||||
|
57
project.py
57
project.py
@ -69,27 +69,6 @@ def not_rev(r):
|
||||
def sq(r):
|
||||
return "'" + r.replace("'", "'\''") + "'"
|
||||
|
||||
_project_hook_list = None
|
||||
def _ProjectHooks():
|
||||
"""List the hooks present in the 'hooks' directory.
|
||||
|
||||
These hooks are project hooks and are copied to the '.git/hooks' directory
|
||||
of all subprojects.
|
||||
|
||||
This function caches the list of hooks (based on the contents of the
|
||||
'repo/hooks' directory) on the first call.
|
||||
|
||||
Returns:
|
||||
A list of absolute paths to all of the files in the hooks directory.
|
||||
"""
|
||||
global _project_hook_list
|
||||
if _project_hook_list is None:
|
||||
d = os.path.realpath(os.path.abspath(os.path.dirname(__file__)))
|
||||
d = os.path.join(d, 'hooks')
|
||||
_project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
|
||||
return _project_hook_list
|
||||
|
||||
|
||||
class DownloadedChange(object):
|
||||
_commit_cache = None
|
||||
|
||||
@ -2106,7 +2085,7 @@ class Project(object):
|
||||
if GitCommand(self, cmd).Wait() != 0:
|
||||
raise GitError('%s merge %s ' % (self.name, head))
|
||||
|
||||
def _InitGitDir(self, mirror_git=None):
|
||||
def _InitGitDir(self, mirror_git=None, MirrorOverride=False):
|
||||
if not os.path.exists(self.gitdir):
|
||||
|
||||
# Initialize the bare repository, which contains all of the objects.
|
||||
@ -2148,11 +2127,38 @@ class Project(object):
|
||||
for key in ['user.name', 'user.email']:
|
||||
if m.Has(key, include_defaults=False):
|
||||
self.config.SetString(key, m.GetString(key))
|
||||
if self.manifest.IsMirror:
|
||||
if self.manifest.IsMirror and not MirrorOverride:
|
||||
self.config.SetString('core.bare', 'true')
|
||||
else:
|
||||
self.config.SetString('core.bare', None)
|
||||
|
||||
def _ProjectHooks(self, remote, repodir):
|
||||
"""List the hooks present in the 'hooks' directory.
|
||||
|
||||
These hooks are project hooks and are copied to the '.git/hooks' directory
|
||||
of all subprojects.
|
||||
|
||||
The remote projecthooks supplement/overrule any stockhook making it possible to
|
||||
have a combination of hooks both from the remote projecthook and
|
||||
.repo/hooks directories.
|
||||
|
||||
Returns:
|
||||
A list of absolute paths to all of the files in the hooks directory and
|
||||
projecthooks files, excluding the .git folder.
|
||||
"""
|
||||
hooks = {}
|
||||
d = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'hooks')
|
||||
hooks = dict([(x, os.path.join(d, x)) for x in os.listdir(d)])
|
||||
if remote is not None:
|
||||
if remote.projecthookName is not None:
|
||||
d = os.path.abspath('%s/projecthooks/%s/%s' % (repodir, remote.name, remote.projecthookName))
|
||||
if os.path.isdir(d):
|
||||
hooks.update(dict([(x, os.path.join(d, x)) for x in os.listdir(d)]))
|
||||
|
||||
if hooks.has_key('.git'):
|
||||
del hooks['.git']
|
||||
return hooks.values()
|
||||
|
||||
def _UpdateHooks(self):
|
||||
if os.path.exists(self.gitdir):
|
||||
self._InitHooks()
|
||||
@ -2161,7 +2167,10 @@ class Project(object):
|
||||
hooks = os.path.realpath(self._gitdir_path('hooks'))
|
||||
if not os.path.exists(hooks):
|
||||
os.makedirs(hooks)
|
||||
for stock_hook in _ProjectHooks():
|
||||
pr = None
|
||||
if self is not self.manifest.manifestProject:
|
||||
pr = self.manifest.remotes.get(self.remote.name)
|
||||
for stock_hook in self._ProjectHooks(pr, self.manifest.repodir):
|
||||
name = os.path.basename(stock_hook)
|
||||
|
||||
if name in ('commit-msg',) and not self.remote.review \
|
||||
|
@ -32,7 +32,7 @@ else:
|
||||
from color import Coloring
|
||||
from command import InteractiveCommand, MirrorSafeCommand
|
||||
from error import ManifestParseError
|
||||
from project import SyncBuffer
|
||||
from project import SyncBuffer, MetaProject
|
||||
from git_config import GitConfig
|
||||
from git_command import git_require, MIN_GIT_VERSION
|
||||
|
||||
@ -374,6 +374,52 @@ to update the working directory files.
|
||||
print(' rm -r %s/.repo' % self.manifest.topdir)
|
||||
print('and try again.')
|
||||
|
||||
def _SyncProjectHooks(self, opt, repodir):
|
||||
"""Downloads the defined hooks supplied in the projecthooks element
|
||||
|
||||
"""
|
||||
# Always delete projecthooks and re-download for every new init.
|
||||
projecthooksdir = os.path.join(repodir, 'projecthooks')
|
||||
if os.path.exists(projecthooksdir):
|
||||
shutil.rmtree(projecthooksdir)
|
||||
for remotename in self.manifest.remotes:
|
||||
r = self.manifest.remotes.get(remotename)
|
||||
if r.projecthookName is not None and r.projecthookRevision is not None:
|
||||
projecthookurl = r.resolvedFetchUrl.rstrip('/') + '/' + r.projecthookName
|
||||
|
||||
ph = MetaProject(manifest = self.manifest,
|
||||
name = r.projecthookName,
|
||||
gitdir = os.path.join(projecthooksdir,'%s/%s.git' % (remotename, r.projecthookName)),
|
||||
worktree = os.path.join(projecthooksdir,'%s/%s' % (remotename, r.projecthookName)))
|
||||
|
||||
ph.revisionExpr = r.projecthookRevision
|
||||
is_new = not ph.Exists
|
||||
|
||||
if is_new:
|
||||
if not opt.quiet:
|
||||
print('Get projecthook %s' % \
|
||||
GitConfig.ForUser().UrlInsteadOf(projecthookurl), file=sys.stderr)
|
||||
ph._InitGitDir(MirrorOverride=True)
|
||||
|
||||
phr = ph.GetRemote(remotename)
|
||||
phr.name = 'origin'
|
||||
phr.url = projecthookurl
|
||||
phr.ResetFetch()
|
||||
phr.Save()
|
||||
|
||||
if not ph.Sync_NetworkHalf(quiet=opt.quiet, is_new=is_new, clone_bundle=False):
|
||||
print('fatal: cannot obtain projecthook %s' % phr.url, file=sys.stderr)
|
||||
|
||||
# Better delete the git dir if we created it; otherwise next
|
||||
# time (when user fixes problems) we won't go through the "is_new" logic.
|
||||
if is_new:
|
||||
shutil.rmtree(ph.gitdir)
|
||||
sys.exit(1)
|
||||
|
||||
syncbuf = SyncBuffer(ph.config)
|
||||
ph.Sync_LocalHalf(syncbuf)
|
||||
syncbuf.Finish()
|
||||
|
||||
def Execute(self, opt, args):
|
||||
git_require(MIN_GIT_VERSION, fail=True)
|
||||
|
||||
@ -389,6 +435,7 @@ to update the working directory files.
|
||||
|
||||
self._SyncManifest(opt)
|
||||
self._LinkManifest(opt.manifest_name)
|
||||
self._SyncProjectHooks(opt, self.manifest.repodir)
|
||||
|
||||
if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror:
|
||||
if opt.config_name or self._ShouldConfigureUser():
|
||||
|
Loading…
Reference in New Issue
Block a user