mirror of
https://gerrit.googlesource.com/git-repo
synced 2025-01-08 16:14:26 +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 notice (#PCDATA)>
|
||||||
|
|
||||||
<!ELEMENT remote (EMPTY)>
|
<!ELEMENT remote (projecthook?)>
|
||||||
<!ATTLIST remote name ID #REQUIRED>
|
<!ATTLIST remote name ID #REQUIRED>
|
||||||
<!ATTLIST remote alias CDATA #IMPLIED>
|
<!ATTLIST remote alias CDATA #IMPLIED>
|
||||||
<!ATTLIST remote fetch CDATA #REQUIRED>
|
<!ATTLIST remote fetch CDATA #REQUIRED>
|
||||||
@ -73,6 +73,10 @@ following DTD:
|
|||||||
<!ATTLIST extend-project path CDATA #IMPLIED>
|
<!ATTLIST extend-project path CDATA #IMPLIED>
|
||||||
<!ATTLIST extend-project groups 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)>
|
<!ELEMENT remove-project (EMPTY)>
|
||||||
<!ATTLIST remove-project name CDATA #REQUIRED>
|
<!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
|
Attribute `name`: the manifest to include, specified relative to
|
||||||
the manifest repository's root.
|
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
|
Local Manifests
|
||||||
===============
|
===============
|
||||||
|
@ -64,7 +64,9 @@ class _XmlRemote(object):
|
|||||||
fetch=None,
|
fetch=None,
|
||||||
manifestUrl=None,
|
manifestUrl=None,
|
||||||
review=None,
|
review=None,
|
||||||
revision=None):
|
revision=None,
|
||||||
|
projecthookName=None,
|
||||||
|
projecthookRevision=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.fetchUrl = fetch
|
self.fetchUrl = fetch
|
||||||
self.manifestUrl = manifestUrl
|
self.manifestUrl = manifestUrl
|
||||||
@ -72,6 +74,8 @@ class _XmlRemote(object):
|
|||||||
self.reviewUrl = review
|
self.reviewUrl = review
|
||||||
self.revision = revision
|
self.revision = revision
|
||||||
self.resolvedFetchUrl = self._resolveFetchUrl()
|
self.resolvedFetchUrl = self._resolveFetchUrl()
|
||||||
|
self.projecthookName = projecthookName
|
||||||
|
self.projecthookRevision = projecthookRevision
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.__dict__ == other.__dict__
|
return self.__dict__ == other.__dict__
|
||||||
@ -167,6 +171,11 @@ class XmlManifest(object):
|
|||||||
e.setAttribute('review', r.reviewUrl)
|
e.setAttribute('review', r.reviewUrl)
|
||||||
if r.revision is not None:
|
if r.revision is not None:
|
||||||
e.setAttribute('revision', r.revision)
|
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):
|
def _ParseGroups(self, groups):
|
||||||
return [x for x in re.split(r'[,\s]+', groups) if x]
|
return [x for x in re.split(r'[,\s]+', groups) if x]
|
||||||
@ -629,7 +638,13 @@ class XmlManifest(object):
|
|||||||
if revision == '':
|
if revision == '':
|
||||||
revision = None
|
revision = None
|
||||||
manifestUrl = self.manifestProject.config.GetString('remote.origin.url')
|
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):
|
def _ParseDefault(self, node):
|
||||||
"""
|
"""
|
||||||
@ -933,3 +948,8 @@ class XmlManifest(object):
|
|||||||
diff['added'].append(toProjects[proj])
|
diff['added'].append(toProjects[proj])
|
||||||
|
|
||||||
return diff
|
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):
|
def sq(r):
|
||||||
return "'" + r.replace("'", "'\''") + "'"
|
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):
|
class DownloadedChange(object):
|
||||||
_commit_cache = None
|
_commit_cache = None
|
||||||
|
|
||||||
@ -2106,7 +2085,7 @@ class Project(object):
|
|||||||
if GitCommand(self, cmd).Wait() != 0:
|
if GitCommand(self, cmd).Wait() != 0:
|
||||||
raise GitError('%s merge %s ' % (self.name, head))
|
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):
|
if not os.path.exists(self.gitdir):
|
||||||
|
|
||||||
# Initialize the bare repository, which contains all of the objects.
|
# Initialize the bare repository, which contains all of the objects.
|
||||||
@ -2148,11 +2127,38 @@ class Project(object):
|
|||||||
for key in ['user.name', 'user.email']:
|
for key in ['user.name', 'user.email']:
|
||||||
if m.Has(key, include_defaults=False):
|
if m.Has(key, include_defaults=False):
|
||||||
self.config.SetString(key, m.GetString(key))
|
self.config.SetString(key, m.GetString(key))
|
||||||
if self.manifest.IsMirror:
|
if self.manifest.IsMirror and not MirrorOverride:
|
||||||
self.config.SetString('core.bare', 'true')
|
self.config.SetString('core.bare', 'true')
|
||||||
else:
|
else:
|
||||||
self.config.SetString('core.bare', None)
|
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):
|
def _UpdateHooks(self):
|
||||||
if os.path.exists(self.gitdir):
|
if os.path.exists(self.gitdir):
|
||||||
self._InitHooks()
|
self._InitHooks()
|
||||||
@ -2161,7 +2167,10 @@ class Project(object):
|
|||||||
hooks = os.path.realpath(self._gitdir_path('hooks'))
|
hooks = os.path.realpath(self._gitdir_path('hooks'))
|
||||||
if not os.path.exists(hooks):
|
if not os.path.exists(hooks):
|
||||||
os.makedirs(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)
|
name = os.path.basename(stock_hook)
|
||||||
|
|
||||||
if name in ('commit-msg',) and not self.remote.review \
|
if name in ('commit-msg',) and not self.remote.review \
|
||||||
|
@ -32,7 +32,7 @@ else:
|
|||||||
from color import Coloring
|
from color import Coloring
|
||||||
from command import InteractiveCommand, MirrorSafeCommand
|
from command import InteractiveCommand, MirrorSafeCommand
|
||||||
from error import ManifestParseError
|
from error import ManifestParseError
|
||||||
from project import SyncBuffer
|
from project import SyncBuffer, MetaProject
|
||||||
from git_config import GitConfig
|
from git_config import GitConfig
|
||||||
from git_command import git_require, MIN_GIT_VERSION
|
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(' rm -r %s/.repo' % self.manifest.topdir)
|
||||||
print('and try again.')
|
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):
|
def Execute(self, opt, args):
|
||||||
git_require(MIN_GIT_VERSION, fail=True)
|
git_require(MIN_GIT_VERSION, fail=True)
|
||||||
|
|
||||||
@ -389,6 +435,7 @@ to update the working directory files.
|
|||||||
|
|
||||||
self._SyncManifest(opt)
|
self._SyncManifest(opt)
|
||||||
self._LinkManifest(opt.manifest_name)
|
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 os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror:
|
||||||
if opt.config_name or self._ShouldConfigureUser():
|
if opt.config_name or self._ShouldConfigureUser():
|
||||||
|
Loading…
Reference in New Issue
Block a user