From fe4a1c7a5d1f4cd8076caed47ae49095deda19ff Mon Sep 17 00:00:00 2001 From: Arun Bose Date: Thu, 16 Jan 2025 15:46:51 +0530 Subject: [PATCH] Git metrics (#2062) * chore: add git metrics * chore: git branch operations metrics * fix: refersh git used to send git project metrics without project switch * chore: git commit metrics * chore: metrics for git next, prev change and history * chore: all git metrics done --- src/extensions/default/Git/src/Branch.js | 25 ++++++--- .../default/Git/src/ErrorHandler.js | 21 ++++--- .../default/Git/src/EventEmitter.js | 8 ++- src/extensions/default/Git/src/History.js | 3 + .../default/Git/src/HistoryViewer.js | 2 + src/extensions/default/Git/src/Main.js | 9 +++ src/extensions/default/Git/src/NoRepo.js | 24 +++++--- src/extensions/default/Git/src/Panel.js | 56 +++++++++++++++---- src/extensions/default/Git/src/Remotes.js | 17 ++++-- src/extensions/default/Git/src/Utils.js | 5 +- .../default/Git/src/dialogs/RemoteCommon.js | 2 +- src/extensions/default/Git/src/utils/Setup.js | 5 +- src/utils/Metrics.js | 32 ++++++++++- 13 files changed, 163 insertions(+), 46 deletions(-) diff --git a/src/extensions/default/Git/src/Branch.js b/src/extensions/default/Git/src/Branch.js index 789d4a38ac..851d5bd8ac 100644 --- a/src/extensions/default/Git/src/Branch.js +++ b/src/extensions/default/Git/src/Branch.js @@ -12,6 +12,7 @@ define(function (require, exports) { StringUtils = brackets.getModule("utils/StringUtils"), DocumentManager = brackets.getModule("document/DocumentManager"), Strings = brackets.getModule("strings"), + Metrics = brackets.getModule("utils/Metrics"), MainViewManager = brackets.getModule("view/MainViewManager"); var Git = require("src/git/Git"), @@ -19,7 +20,7 @@ define(function (require, exports) { EventEmitter = require("src/EventEmitter"), ErrorHandler = require("src/ErrorHandler"), Panel = require("src/Panel"), - Setup = require("src/utils/Setup"), + Setup = require("src/utils/Setup"), Preferences = require("src/Preferences"), ProgressDialog = require("src/dialogs/Progress"), Utils = require("src/Utils"), @@ -105,22 +106,25 @@ define(function (require, exports) { if (useRebase) { Git.rebaseInit(fromBranch).catch(function (err) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'rebase', "fail"); throw ErrorHandler.showError(err, Strings.ERROR_REBASE_FAILED); }).then(function (stdout) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'rebase', "success"); Utils.showOutput(stdout || Strings.GIT_REBASE_SUCCESS, Strings.REBASE_RESULT).finally(function () { EventEmitter.emit(Events.REFRESH_ALL); }); - - }); + }).catch(console.error); } else { Git.mergeBranch(fromBranch, mergeMsg, useNoff).catch(function (err) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'merge', "fail"); throw ErrorHandler.showError(err, Strings.ERROR_MERGE_FAILED); }).then(function (stdout) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'merge', "success"); Utils.showOutput(stdout || Strings.GIT_MERGE_SUCCESS, Strings.MERGE_RESULT).finally(function () { EventEmitter.emit(Events.REFRESH_ALL); }); - }); + }).catch(console.error); } } }); @@ -218,8 +222,10 @@ define(function (require, exports) { track = !!isRemote; Git.createBranch(branchName, originName, track).catch(function (err) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'branch', "createFail"); throw ErrorHandler.showError(err, Strings.ERROR_CREATE_BRANCH); }).then(function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'branch', "create"); EventEmitter.emit(Events.REFRESH_ALL); }); } @@ -246,8 +252,10 @@ define(function (require, exports) { }).then(function (response) { if (response === true) { return Git.forceBranchDelete(branchName).then(function (output) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'branch', "delete"); return Utils.showOutput(output || Strings.GIT_BRANCH_DELETE_SUCCESS); }).catch(function (err) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'branch', "deleteFail"); ErrorHandler.showError(err, Strings.ERROR_BRANCH_DELETE_FORCED); }); } @@ -273,12 +281,15 @@ define(function (require, exports) { Git.getCurrentBranchName().then(function (oldBranchName) { Git.checkout(newBranchName).then(function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'branch', "switch"); return closeNotExistingFiles(oldBranchName, newBranchName); }).catch(function (err) { - throw ErrorHandler.showError(err, Strings.ERROR_SWITCHING_BRANCHES); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'branch', "switchFail"); + ErrorHandler.showError(err, Strings.ERROR_SWITCHING_BRANCHES); }); }).catch(function (err) { - throw ErrorHandler.showError(err, Strings.ERROR_GETTING_CURRENT_BRANCH); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'branch', "switchFail"); + ErrorHandler.showError(err, Strings.ERROR_GETTING_CURRENT_BRANCH); }); }); @@ -483,7 +494,7 @@ define(function (require, exports) { } }); }).catch(function (err) { - throw ErrorHandler.showError(err); + ErrorHandler.showError(err); }); } diff --git a/src/extensions/default/Git/src/ErrorHandler.js b/src/extensions/default/Git/src/ErrorHandler.js index 15ddbfbf90..53e6e4d402 100644 --- a/src/extensions/default/Git/src/ErrorHandler.js +++ b/src/extensions/default/Git/src/ErrorHandler.js @@ -1,15 +1,12 @@ define(function (require, exports) { - var _ = brackets.getModule("thirdparty/lodash"), - Dialogs = brackets.getModule("widgets/Dialogs"), + const Dialogs = brackets.getModule("widgets/Dialogs"), Mustache = brackets.getModule("thirdparty/mustache/mustache"), - NativeApp = brackets.getModule("utils/NativeApp"), + Metrics = brackets.getModule("utils/Metrics"), Strings = brackets.getModule("strings"), Utils = require("src/Utils"), errorDialogTemplate = require("text!templates/git-error-dialog.html"); - var errorQueue = []; - function errorToString(err) { return Utils.encodeSensitiveInformation(err.toString()); } @@ -34,13 +31,21 @@ define(function (require, exports) { }; exports.logError = function (err) { - var msg = err && err.stack ? err.stack : err; + const msg = err && err.stack ? err.stack : err; Utils.consoleError("[brackets-git] " + msg); - errorQueue.push(err); return err; }; - exports.showError = function (err, title, dontStripError) { + /** + * + * @param err + * @param title + * @param {dontStripError: boolean, errorMetric: string} options + */ + exports.showError = function (err, title, options = {}) { + const dontStripError = options.dontStripError; + const errorMetric = options.errorMetric; + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'dialogErr', errorMetric || "Show"); if (err.__shown) { return err; } exports.logError(err); diff --git a/src/extensions/default/Git/src/EventEmitter.js b/src/extensions/default/Git/src/EventEmitter.js index 874d3ba3d4..a6253b4f7e 100644 --- a/src/extensions/default/Git/src/EventEmitter.js +++ b/src/extensions/default/Git/src/EventEmitter.js @@ -1,15 +1,19 @@ define(function (require, exports, module) { - const EventDispatcher = brackets.getModule("utils/EventDispatcher"); + const EventDispatcher = brackets.getModule("utils/EventDispatcher"), + Metrics = brackets.getModule("utils/Metrics"); const emInstance = {}; EventDispatcher.makeEventDispatcher(emInstance); - function getEmitter(eventName) { + function getEmitter(eventName, optionalMetricToLog) { if (!eventName) { throw new Error("no event has been passed to get the emittor!"); } return function () { emit(eventName, ...arguments); + if(optionalMetricToLog) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, optionalMetricToLog[0], optionalMetricToLog[1]); + } }; } diff --git a/src/extensions/default/Git/src/History.js b/src/extensions/default/Git/src/History.js index b9e1a46f3d..bb1dd6d61c 100644 --- a/src/extensions/default/Git/src/History.js +++ b/src/extensions/default/Git/src/History.js @@ -6,6 +6,7 @@ define(function (require) { FileUtils = brackets.getModule("file/FileUtils"), LocalizationUtils = brackets.getModule("utils/LocalizationUtils"), Strings = brackets.getModule("strings"), + Metrics = brackets.getModule("utils/Metrics"), Mustache = brackets.getModule("thirdparty/mustache/mustache"); // Local modules @@ -315,9 +316,11 @@ define(function (require) { }); EventEmitter.on(Events.HISTORY_SHOW_FILE, function () { handleToggleHistory("FILE"); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'panel', "fileHistory"); }); EventEmitter.on(Events.HISTORY_SHOW_GLOBAL, function () { handleToggleHistory("GLOBAL"); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'panel', "history"); }); EventEmitter.on(Events.REFRESH_HISTORY, function () { handleToggleHistory("REFRESH"); diff --git a/src/extensions/default/Git/src/HistoryViewer.js b/src/extensions/default/Git/src/HistoryViewer.js index 887f873471..b0d3e67f27 100644 --- a/src/extensions/default/Git/src/HistoryViewer.js +++ b/src/extensions/default/Git/src/HistoryViewer.js @@ -5,6 +5,7 @@ define(function (require, exports) { Mustache = brackets.getModule("thirdparty/mustache/mustache"), WorkspaceManager = brackets.getModule("view/WorkspaceManager"), Strings = brackets.getModule("strings"), + Metrics = brackets.getModule("utils/Metrics"), marked = brackets.getModule('thirdparty/marked.min').marked; const ErrorHandler = require("src/ErrorHandler"), @@ -232,6 +233,7 @@ define(function (require, exports) { } function show(commitInfo, doc, options) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'history', "detailView"); initialize(); commit = commitInfo; diff --git a/src/extensions/default/Git/src/Main.js b/src/extensions/default/Git/src/Main.js index a4d6db2100..8f6aa6439c 100644 --- a/src/extensions/default/Git/src/Main.js +++ b/src/extensions/default/Git/src/Main.js @@ -6,6 +6,7 @@ define(function (require, exports) { Menus = brackets.getModule("command/Menus"), FileSystem = brackets.getModule("filesystem/FileSystem"), Mustache = brackets.getModule("thirdparty/mustache/mustache"), + Metrics = brackets.getModule("utils/Metrics"), ProjectManager = brackets.getModule("project/ProjectManager"); const Constants = require("src/Constants"), @@ -418,9 +419,17 @@ define(function (require, exports) { $(window).focus(refreshOnFocusChange); // Event handlers + let projectSwitched = true; + EventEmitter.on(Events.BRACKETS_PROJECT_CHANGE, function () { + // pressing refresh button will raise GIT_ENABLED event and we only want one enabled metric + // per project open. + projectSwitched = true; + }); EventEmitter.on(Events.GIT_ENABLED, function () { _enableAllCommands(true); gitEnabled = true; + projectSwitched && Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'enabled', "project"); + projectSwitched = false; }); EventEmitter.on(Events.GIT_DISABLED, function () { _enableAllCommands(false); diff --git a/src/extensions/default/Git/src/NoRepo.js b/src/extensions/default/Git/src/NoRepo.js index d0746e7e2e..5c6d5cc0cb 100644 --- a/src/extensions/default/Git/src/NoRepo.js +++ b/src/extensions/default/Git/src/NoRepo.js @@ -2,17 +2,18 @@ define(function (require) { // Brackets modules - const FileSystem = brackets.getModule("filesystem/FileSystem"), + const FileSystem = brackets.getModule("filesystem/FileSystem"), FileUtils = brackets.getModule("file/FileUtils"), ProjectManager = brackets.getModule("project/ProjectManager"), CommandManager = brackets.getModule("command/CommandManager"), - StringUtils = brackets.getModule("utils/StringUtils"); + Metrics = brackets.getModule("utils/Metrics"), + Strings = brackets.getModule("strings"), + StringUtils = brackets.getModule("utils/StringUtils"); // Local modules - const ErrorHandler = require("src/ErrorHandler"), + const ErrorHandler = require("src/ErrorHandler"), Events = require("src/Events"), EventEmitter = require("src/EventEmitter"), - Strings = brackets.getModule("strings"), ExpectedError = require("src/ExpectedError"), ProgressDialog = require("src/dialogs/Progress"), CloneDialog = require("src/dialogs/Clone"), @@ -58,8 +59,8 @@ define(function (require) { EventEmitter.emit(Events.GIT_CHANGE_EMAIL, function () { Git.init().then(function (result) { resolve(result); - }).catch(function (err) { - reject(err); + }).catch(function (error) { + reject(error); }); }); }); @@ -70,9 +71,11 @@ define(function (require) { }); }); }).then(function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'init', "success"); return stageGitIgnore("Initial staging"); }).catch(function (err) { - ErrorHandler.showError(err, Strings.INIT_NEW_REPO_FAILED, true); + ErrorHandler.showError(err, Strings.INIT_NEW_REPO_FAILED, {dontStripError: true}); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'init', "fail"); }).then(function () { EventEmitter.emit(Events.REFRESH_ALL); }); @@ -99,7 +102,7 @@ define(function (require) { const clonePath = Phoenix.app.getDisplayPath(Utils.getProjectRoot()); const err = new ExpectedError( StringUtils.format(Strings.GIT_CLONE_ERROR_EXPLAIN, clonePath)); - ErrorHandler.showError(err, Strings.GIT_CLONE_REMOTE_FAILED, true); + ErrorHandler.showError(err, Strings.GIT_CLONE_REMOTE_FAILED, {dontStripError: true}); return; } function _clone(cloneConfig) { @@ -115,8 +118,11 @@ define(function (require) { const tracker = ProgressDialog.newProgressTracker(); destPath = destPath ? fs.getTauriPlatformPath(destPath) : "."; return ProgressDialog.show(Git.clone(remoteUrl, destPath, tracker), tracker); + }).then(()=>{ + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'clone', "success"); }).catch(function (err) { - ErrorHandler.showError(err, Strings.GIT_CLONE_REMOTE_FAILED); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'clone', "fail"); + ErrorHandler.showError(err, Strings.GIT_CLONE_REMOTE_FAILED, {errorMetric: "clone"}); }); // restore original url if desired diff --git a/src/extensions/default/Git/src/Panel.js b/src/extensions/default/Git/src/Panel.js index 758e976a3b..ed71310eb9 100644 --- a/src/extensions/default/Git/src/Panel.js +++ b/src/extensions/default/Git/src/Panel.js @@ -18,6 +18,7 @@ define(function (require, exports) { ProjectManager = brackets.getModule("project/ProjectManager"), StringUtils = brackets.getModule("utils/StringUtils"), Strings = brackets.getModule("strings"), + Metrics = brackets.getModule("utils/Metrics"), Constants = require("src/Constants"), Git = require("src/git/Git"), Events = require("./Events"), @@ -87,6 +88,8 @@ define(function (require, exports) { const compiledTemplate = Mustache.render(gitCommitDialogTemplate, {Strings: Strings}), dialog = Dialogs.showModalDialogUsingTemplate(compiledTemplate), $dialog = dialog.getElement(); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'commit', "showDialog"); + let totalLintErrors = 0; inspectFiles(files, $dialog).then(function (lintResults) { // Flatten the error structure from various providers lintResults = lintResults || []; @@ -111,8 +114,11 @@ define(function (require, exports) { ErrorHandler.logError("[brackets-git] lintResults contain object in unexpected format: " + JSON.stringify(lintResult)); } lintResult.hasErrors = lintResult.errors.length > 0; + totalLintErrors += lintResult.errors.length; }); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'commit', "lintErr" + Metrics.getRangeName(totalLintErrors)); + // Filter out only results with errors to show lintResults = _.filter(lintResults, function (lintResult) { return lintResult.hasErrors; @@ -152,7 +158,11 @@ define(function (require, exports) { _makeDialogBig($dialog); // Show nicely colored commit diff - $dialog.find(".commit-diff").append(Utils.formatDiff(stagedDiff)); + const diff = Utils.formatDiff(stagedDiff); + if(diff === Utils.FORMAT_DIFF_TOO_LARGE) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'commit', "diffTooLarge"); + } + $dialog.find(".commit-diff").append(diff); // Enable / Disable amend checkbox var toggleAmendCheckbox = function (bool) { @@ -331,6 +341,8 @@ define(function (require, exports) { } else { throw new ExpectedError(Strings.ERROR_MODIFIED_DIALOG_FILES); } + }).then(()=>{ + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'commit', "success"); }).catch(function (err) { if (ErrorHandler.contains(err, "Please tell me who you are")) { return new Promise((resolve)=>{ @@ -342,7 +354,8 @@ define(function (require, exports) { }); } - ErrorHandler.showError(err, Strings.ERROR_GIT_COMMIT_FAILED); + ErrorHandler.showError(err, Strings.ERROR_GIT_COMMIT_FAILED, {errorMetric: "commit"}); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'commit', "fail"); }).finally(function () { EventEmitter.emit(Events.GIT_COMMITED); @@ -441,8 +454,15 @@ define(function (require, exports) { dialog = Dialogs.showModalDialogUsingTemplate(compiledTemplate), $dialog = dialog.getElement(); _makeDialogBig($dialog); - $dialog.find(".commit-diff").append(Utils.formatDiff(diff)); + const diffVal = Utils.formatDiff(diff); + if(diffVal === Utils.FORMAT_DIFF_TOO_LARGE) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'diffBtn', "diffTooLarge"); + } else { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'diffBtn', "success"); + } + $dialog.find(".commit-diff").append(Utils.formatDiff(diffVal)); }).catch(function (err) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'diffBtn', "error"); ErrorHandler.showError(err, Strings.ERROR_GIT_DIFF_FAILED); }); } @@ -599,8 +619,8 @@ define(function (require, exports) { function inspectFiles(gitStatusResults, $dialog) { const lintResults = []; let totalFiles = gitStatusResults.length, + totalFilesLinted = 0, filesDone = 0; - function showProgress() { const $progressBar = $dialog.find('.accordion-progress-bar-inner'); if ($progressBar.length) { @@ -651,6 +671,7 @@ define(function (require, exports) { resolve(); }).finally(()=>{ filesDone++; + totalFilesLinted++; showProgress(); }); }, 0); // Delay of 0ms to defer to the next tick of the event loop @@ -658,6 +679,8 @@ define(function (require, exports) { }); return Promise.all(_.compact(codeInspectionPromises)).then(function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'commit', "files" + Metrics.getRangeName(totalFiles)); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'commit', "lint" + Metrics.getRangeName(totalFilesLinted)); return lintResults; }); } @@ -1214,7 +1237,7 @@ define(function (require, exports) { }); } }) - .on("click", ".git-refresh", EventEmitter.getEmitter(Events.REFRESH_ALL)) + .on("click", ".git-refresh", EventEmitter.getEmitter(Events.REFRESH_ALL, ["panel", "refreshBtn"])) .on("click", ".git-commit", EventEmitter.getEmitter(Events.HANDLE_GIT_COMMIT)) .on("click", ".git-rebase-continue", function (e) { handleRebase("continue", e); }) .on("click", ".git-rebase-skip", function (e) { handleRebase("skip", e); }) @@ -1222,26 +1245,34 @@ define(function (require, exports) { .on("click", ".git-commit-merge", commitMerge) .on("click", ".git-merge-abort", abortMerge) .on("click", ".git-find-conflicts", findConflicts) - .on("click", ".git-prev-gutter", GutterManager.goToPrev) - .on("click", ".git-next-gutter", GutterManager.goToNext) + .on("click", ".git-prev-gutter", ()=>{ + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'panel', "prevBtn"); + GutterManager.goToPrev(); + }) + .on("click", ".git-next-gutter", ()=>{ + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'panel', "nextBtn"); + GutterManager.goToNext(); + }) .on("click", ".git-file-history", EventEmitter.getEmitter(Events.HISTORY_SHOW_FILE)) .on("click", ".git-history-toggle", EventEmitter.getEmitter(Events.HISTORY_SHOW_GLOBAL)) - .on("click", ".git-fetch", EventEmitter.getEmitter(Events.HANDLE_FETCH)) + .on("click", ".git-fetch", EventEmitter.getEmitter(Events.HANDLE_FETCH, ["panel", "fetchBtn"])) .on("click", ".git-push", function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'panel', "pushBtn"); var typeOfRemote = $(this).attr("x-selected-remote-type"); if (typeOfRemote === "git") { EventEmitter.emit(Events.HANDLE_PUSH); } }) - .on("click", ".git-pull", EventEmitter.getEmitter(Events.HANDLE_PULL)) + .on("click", ".git-pull", EventEmitter.getEmitter(Events.HANDLE_PULL, ["panel", "pullBtn"])) .on("click", ".git-init", EventEmitter.getEmitter(Events.HANDLE_GIT_INIT)) .on("click", ".git-clone", EventEmitter.getEmitter(Events.HANDLE_GIT_CLONE)) - .on("click", ".change-remote", EventEmitter.getEmitter(Events.HANDLE_REMOTE_PICK)) - .on("click", ".remove-remote", EventEmitter.getEmitter(Events.HANDLE_REMOTE_DELETE)) - .on("click", ".git-remote-new", EventEmitter.getEmitter(Events.HANDLE_REMOTE_CREATE)) + .on("click", ".change-remote", EventEmitter.getEmitter(Events.HANDLE_REMOTE_PICK, ["panel", "changeRemote"])) + .on("click", ".remove-remote", EventEmitter.getEmitter(Events.HANDLE_REMOTE_DELETE, ["panel", "removeRemote"])) + .on("click", ".git-remote-new", EventEmitter.getEmitter(Events.HANDLE_REMOTE_CREATE, ["panel", "newRemote"])) .on("contextmenu", "tr", function (e) { const $this = $(this); if ($this.hasClass("history-commit")) { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'cmenu', "history"); if(!$this.hasClass("selected")){ $this.click(); } @@ -1251,6 +1282,7 @@ define(function (require, exports) { $this.click(); setTimeout(function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'cmenu', "filechanges"); Menus.getContextMenu(Constants.GIT_PANEL_CHANGES_CMENU).open(e); }, 1); }); diff --git a/src/extensions/default/Git/src/Remotes.js b/src/extensions/default/Git/src/Remotes.js index 63230e5c0f..4721b81778 100644 --- a/src/extensions/default/Git/src/Remotes.js +++ b/src/extensions/default/Git/src/Remotes.js @@ -5,6 +5,8 @@ define(function (require) { DefaultDialogs = brackets.getModule("widgets/DefaultDialogs"), Dialogs = brackets.getModule("widgets/Dialogs"), Mustache = brackets.getModule("thirdparty/mustache/mustache"), + Metrics = brackets.getModule("utils/Metrics"), + Strings = brackets.getModule("strings"), StringUtils = brackets.getModule("utils/StringUtils"); // Local modules @@ -16,7 +18,6 @@ define(function (require) { ProgressDialog = require("src/dialogs/Progress"), PullDialog = require("src/dialogs/Pull"), PushDialog = require("src/dialogs/Push"), - Strings = brackets.getModule("strings"), Utils = require("src/Utils"); // Templates @@ -237,11 +238,13 @@ define(function (require) { return ProgressDialog.show(op, progressTracker) .then(function (result) { return ProgressDialog.waitForClose().then(function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'push', "success"); showPushResult(result); }); }) .catch(function (err) { - ErrorHandler.showError(err, Strings.ERROR_PUSHING_REMOTE); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'push', "fail"); + ErrorHandler.showError(err, Strings.ERROR_PUSHING_REMOTE, {errorMetric: "push"}); }); }); // restore original url if desired @@ -315,12 +318,14 @@ define(function (require) { // leaving the result as empty in stdout. // If we reach this point, the command has succeeded, // so we display a success message if `result` is "". + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'pull', "success"); return Utils.showOutput(result || Strings.GIT_PULL_SUCCESS, Strings.GIT_PULL_RESPONSE); }); }) .catch(function (err) { - ErrorHandler.showError(err, Strings.ERROR_PULLING_REMOTE); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'pull', "fail"); + ErrorHandler.showError(err, Strings.ERROR_PULLING_REMOTE, {errorMetric: "pull"}); }); }); // restore original url if desired @@ -347,8 +352,12 @@ define(function (require) { const tracker = ProgressDialog.newProgressTracker(); return ProgressDialog.show(Git.fetchAllRemotes(tracker), tracker) + .then(()=>{ + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'fetch', "success"); + }) .catch(function (err) { - ErrorHandler.showError(err); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'fetch', "fail"); + ErrorHandler.showError(err, undefined, {errorMetric: "fetch"}); }) .then(ProgressDialog.waitForClose) .finally(function () { diff --git a/src/extensions/default/Git/src/Utils.js b/src/extensions/default/Git/src/Utils.js index 6476945df9..d55400bd80 100644 --- a/src/extensions/default/Git/src/Utils.js +++ b/src/extensions/default/Git/src/Utils.js @@ -23,6 +23,8 @@ define(function (require, exports, module) { Constants = require("src/Constants"), Strings = brackets.getModule("strings"); + const FORMAT_DIFF_TOO_LARGE = "
" + Strings.DIFF_TOO_LONG + "
"; + // Module variables const formatDiffTemplate = require("text!templates/format-diff.html"), questionDialogTemplate = require("text!templates/git-question-dialog.html"), @@ -69,7 +71,7 @@ define(function (require, exports, module) { var diffSplit = diff.split("\n"); if (diffSplit.length > DIFF_MAX_LENGTH) { - return "
" + Strings.DIFF_TOO_LONG + "
"; + return "" + FORMAT_DIFF_TOO_LARGE; // create new str to return } diffSplit.forEach(function (line) { @@ -585,6 +587,7 @@ define(function (require, exports, module) { } // Public API + exports.FORMAT_DIFF_TOO_LARGE = FORMAT_DIFF_TOO_LARGE; exports.formatDiff = formatDiff; exports.getProjectRoot = getProjectRoot; exports.getExtensionDirectory = getExtensionDirectory; diff --git a/src/extensions/default/Git/src/dialogs/RemoteCommon.js b/src/extensions/default/Git/src/dialogs/RemoteCommon.js index dee30f9564..89be66d975 100644 --- a/src/extensions/default/Git/src/dialogs/RemoteCommon.js +++ b/src/extensions/default/Git/src/dialogs/RemoteCommon.js @@ -85,7 +85,7 @@ define(function (require, exports) { .then(function () { fillBranches(config, $dialog); }).catch(function (err) { - throw ErrorHandler.showError(err, Strings.ERROR_FETCH_REMOTE); + ErrorHandler.showError(err, Strings.ERROR_FETCH_REMOTE); }); }); fillBranches(config, $dialog); diff --git a/src/extensions/default/Git/src/utils/Setup.js b/src/extensions/default/Git/src/utils/Setup.js index 36ce56c0d6..af109c30b9 100644 --- a/src/extensions/default/Git/src/utils/Setup.js +++ b/src/extensions/default/Git/src/utils/Setup.js @@ -1,7 +1,8 @@ define(function (require, exports) { // Brackets modules - const _ = brackets.getModule("thirdparty/lodash"); + const _ = brackets.getModule("thirdparty/lodash"), + Metrics = brackets.getModule("utils/Metrics"); // Local modules const Cli = require("src/Cli"), @@ -110,10 +111,12 @@ define(function (require, exports) { getGitVersion().then(function (_version) { extensionActivated = true; resolve(extensionActivated); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'installed', "yes"); }).catch(function (err) { extensionActivated = false; console.warn("Failed to launch Git executable. Deactivating Git extension. Is git installed?", err); resolve(extensionActivated); + Metrics.countEvent(Metrics.EVENT_TYPE.GIT, 'installed', "no"); }); }); } diff --git a/src/utils/Metrics.js b/src/utils/Metrics.js index 7dfd498e3f..56fff6fcde 100644 --- a/src/utils/Metrics.js +++ b/src/utils/Metrics.js @@ -85,6 +85,7 @@ define(function (require, exports, module) { * ### Properties * `PLATFORM`, `PROJECT`, `THEMES`, `EXTENSIONS`, `EXTENSIONS`, `UI`, `UI_DIALOG`, `UI_BOTTOM_PANEL`, * `UI_SIDE_PANEL`, `LIVE_PREVIEW`, `CODE_HINTS`, `EDITOR`, `SEARCH`, `SHARING`, `PERFORMANCE`, `NEW_PROJECT` + * `ERROR`, `USER`, `NODEJS`, `LINT`, `GIT` * * @typedef EVENT_TYPE * @type {Object} @@ -114,7 +115,8 @@ define(function (require, exports, module) { ERROR: "error", USER: "user", NODEJS: "node", - LINT: "lint" + LINT: "lint", + GIT: "git" }; /** @@ -424,6 +426,33 @@ define(function (require, exports, module) { valueEvent(EVENT_TYPE.PERFORMANCE, "ms", action, Number(durationMs)); } + /** + * Get the range name for a given number. + * + * The function returns the first range that the number fits into, based on predefined ranges: + * 0, 10, 25, 50, 100, 250, 500, 1000, 5000, 10000, and "10000+" for numbers exceeding 10000. + * + * @param {number} number - The number to determine the range for. + * @returns {string} The range name that the number belongs to. + */ + function getRangeName(number) { + // Define the ranges + const ranges = [0, 5, 10, 25, 50, 100, 250, 500, 1000, 5000, 10000]; + + // Iterate through the ranges and return the first range that is greater than or equal to the number + // small array, linear scan is most efficient than binary search in most cases comparing the overheads and + // maintainability + for (let i = 0; i < ranges.length; i++) { + if (number <= ranges[i]) { + return ""+ranges[i]; + } + } + + // If the number exceeds the largest range, return "10000+" + return "10000+"; + } + + // Define public API exports.init = init; exports.setDisabled = setDisabled; @@ -434,6 +463,7 @@ define(function (require, exports, module) { exports.valueEvent = valueEvent; exports.logPerformanceTime = logPerformanceTime; exports.flushMetrics = flushMetrics; + exports.getRangeName = getRangeName; exports.EVENT_TYPE = EVENT_TYPE; exports.AUDIT_TYPE_COUNT = AUDIT_TYPE_COUNT; exports.AUDIT_TYPE_VALUE = AUDIT_TYPE_VALUE;