diff --git a/lib/utils.js b/lib/utils.js index 6824126..84dd8e2 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -131,30 +131,60 @@ exports.isDir = function (path) { }; +// Given a path, determine if the path is a regular file +exports.isFile = function (path) { + + try { + var stat = Fs.statSync(path); + return stat.isFile(); + } + catch (e) { + return false; + } +}; + + // Given a starting directory, find the root of a git repository. -// In this case, the root is defined as the first directory that contains -// a directory named ".git" // -// Returns a string if found, otherwise undefined +// Returns undefined if not found, otherwise an object containing: +// root: path to the root of the Git repo +// - this is the first directory (traversing upward) that contains a ".git" +// directory (most cases) or a ".git" file (submodule inside another Git repo) +// gitDir: path to the ".git"-ish directory of the Git repo +// - in most cases, this is the ".git" directory inside the Git repo root +// - in the submodule case, this is the directory pointed to by the ".git" file exports.findGitRoot = function (start) { start = start || Path.dirname(internals.findParent(module).filename); var root; + var gitDir = Path.join(start, '.git'); - if (exports.isDir(Path.join(start, '.git'))) { + if (exports.isDir(gitDir)) { + root = start; + } + else if (exports.isFile(gitDir)) { + // This is a git submodule inside another git repo! + // Resolve the path to its ".git"-ish directory. root = start; + gitFile = Fs.readFileSync(gitDir, { encoding: 'utf8' }); + matches = gitFile.split('\n')[0].match(/^gitdir: (.+)$/); + if (!matches) { + return internals.throwWarn('Unable to find a .git directory for this (submodule) project'); + } + // This path is relative to the root of the Git repo. + gitDir = Path.join(root, matches[1]); } /* $lab:coverage:off$ */ // Coverage disabled here due to false positive on else if, since we have to trap the throwWarn method else if (Path.dirname(start) !== start) { - root = exports.findGitRoot(Path.dirname(start)); + return exports.findGitRoot(Path.dirname(start)); } else { return internals.throwWarn('Unable to find a .git directory for this project'); } /* $lab:coverage:on$ */ - return root; + return { root: root, gitDir: gitDir }; }; @@ -191,7 +221,10 @@ exports.findProjectRoot = function (start) { // Returns an array exports.findProjects = function (start, depth) { - start = start || exports.findGitRoot(internals.findParent(module).filename); + if (!start) { + gitRoot = exports.findGitRoot(internals.findParent(module).filename); + start = gitRoot ? gitRoot.root : undefined; + } depth = depth || 0; ++depth; @@ -229,8 +262,8 @@ exports.findProjects = function (start, depth) { exports.installHooks = function (hooks, root) { hooks = Array.isArray(hooks) ? hooks : [hooks]; - var gitRoot = exports.findGitRoot(root); - var hookRoot = Path.join(gitRoot, '.git', 'hooks'); + var hookRoot = Path.join(exports.findGitRoot(root).gitDir, 'hooks'); + var source = Path.resolve(__dirname, '..', 'bin', 'validate.sh'); if (!exports.isDir(hookRoot)) {