diff --git a/src-node/git/processUtils.js b/src-node/git/processUtils.js index e2f3dee2bc..86d760bdc3 100644 --- a/src-node/git/processUtils.js +++ b/src-node/git/processUtils.js @@ -4,8 +4,22 @@ var exec = require("child_process").exec, which = require("which"); var isWin = /^win/.test(process.platform); +var isMac = process.platform === "darwin"; var noop = function () {}; +// Cache for xcode-select CLT check (null = not yet checked) +var _xcodeCliToolsInstalled = null; + +function _isXcodeCliToolsInstalled(callback) { + if (_xcodeCliToolsInstalled !== null) { + return callback(_xcodeCliToolsInstalled); + } + exec("xcode-select -p", function (err) { + _xcodeCliToolsInstalled = !err; + callback(_xcodeCliToolsInstalled); + }); +} + function fixEOL(str) { if (str[str.length - 1] === "\n") { str = str.slice(0, -1); @@ -104,6 +118,17 @@ function executableExists(filename, dir, callback) { var exists = stats.isFile(); if (!exists) { path = undefined; } + // On macOS, /usr/bin/git is a shim that triggers an "Install Xcode CLT" dialog + // when spawned if CLT is not installed. Check for CLT before allowing it. + if (exists && isMac && Path.normalize(path) === "/usr/bin/git") { + return _isXcodeCliToolsInstalled(function (installed) { + if (!installed) { + return callback(null, false, undefined); + } + return callback(null, true, path); + }); + } + return callback(null, exists, path); }); }); diff --git a/src-node/package-lock.json b/src-node/package-lock.json index 459799020e..86b04e13a1 100644 --- a/src-node/package-lock.json +++ b/src-node/package-lock.json @@ -1,12 +1,12 @@ { "name": "@phcode/node-core", - "version": "5.1.3-0", + "version": "5.1.4-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@phcode/node-core", - "version": "5.1.3-0", + "version": "5.1.4-0", "license": "GNU-AGPL3.0", "dependencies": { "@expo/sudo-prompt": "^9.3.2", diff --git a/src/extensions/default/Git/src/git/GitCli.js b/src/extensions/default/Git/src/git/GitCli.js index 39cd9aa56f..16d269776e 100644 --- a/src/extensions/default/Git/src/git/GitCli.js +++ b/src/extensions/default/Git/src/git/GitCli.js @@ -1022,9 +1022,33 @@ define(function (require, exports) { function getGitRoot() { var projectRoot = Utils.getProjectRoot(); - return git(["rev-parse", "--show-toplevel"], { - cwd: fs.getTauriPlatformPath(projectRoot) - }) + + // Quick filesystem pre-check: if .git doesn't exist in the project root, + // skip spawning git entirely. This avoids triggering macOS CLT shim dialogs + // on non-git projects and is a minor optimization on all platforms. + return new Promise(function (resolve) { + var checkPath = projectRoot; + if (strEndsWith(checkPath, "/")) { + checkPath = checkPath.slice(0, -1); + } + if (typeof brackets !== "undefined" && brackets.fs && brackets.fs.stat) { + brackets.fs.stat(checkPath + "/.git", function (err, result) { + var exists = err ? false : (result.isFile() || result.isDirectory()); + resolve(exists); + }); + } else { + FileSystem.resolve(checkPath + "/.git", function (err, item, stat) { + var exists = err ? false : (stat.isFile || stat.isDirectory); + resolve(exists); + }); + } + }).then(function (hasGitDir) { + if (!hasGitDir) { + return null; + } + return git(["rev-parse", "--show-toplevel"], { + cwd: fs.getTauriPlatformPath(projectRoot) + }) .catch(function (e) { if (ErrorHandler.contains(e, "Not a git repository")) { return null; @@ -1095,6 +1119,7 @@ define(function (require, exports) { }); }); + }); } function setTagName(tagname, commitHash) { diff --git a/src/extensions/default/Git/src/utils/Setup.js b/src/extensions/default/Git/src/utils/Setup.js index af109c30b9..a172da134c 100644 --- a/src/extensions/default/Git/src/utils/Setup.js +++ b/src/extensions/default/Git/src/utils/Setup.js @@ -16,9 +16,10 @@ define(function (require, exports) { ]; let standardGitPathsNonWin = [ + "/opt/homebrew/bin/git", // Apple Silicon Homebrew "/usr/local/git/bin/git", "/usr/local/bin/git", - "/usr/bin/git" + "/usr/bin/git" // macOS CLT shim check handled on node side ]; let extensionActivated = false; @@ -27,7 +28,7 @@ define(function (require, exports) { function getGitVersion() { return new Promise(function (resolve, reject) { - // TODO: do this in two steps - first check user config and then check all + // User-configured path gets priority, then "git" (PATH lookup), then standard paths var pathsToLook = [Preferences.get("gitPath"), "git"].concat(brackets.platform === "win" ? standardGitPathsWin : standardGitPathsNonWin); pathsToLook = _.unique(_.compact(pathsToLook));