Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
8
lib/vscode/extensions/git/.vscodeignore
Normal file
@ -0,0 +1,8 @@
|
||||
src/**
|
||||
test/**
|
||||
out/**
|
||||
tsconfig.json
|
||||
build/**
|
||||
extension.webpack.config.js
|
||||
cgmanifest.json
|
||||
yarn.lock
|
20
lib/vscode/extensions/git/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Git integration for Visual Studio Code
|
||||
|
||||
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
||||
|
||||
## Features
|
||||
|
||||
See [Git support in VS Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support) to learn about the features of this extension.
|
||||
|
||||
## API
|
||||
|
||||
The Git extension exposes an API, reachable by any other extension.
|
||||
|
||||
1. Copy `src/api/git.d.ts` to your extension's sources;
|
||||
2. Include `git.d.ts` in your extension's compilation.
|
||||
3. Get a hold of the API with the following snippet:
|
||||
|
||||
```ts
|
||||
const gitExtension = vscode.extensions.getExtension<GitExtension>('vscode.git').exports;
|
||||
const git = gitExtension.getAPI(1);
|
||||
```
|
101
lib/vscode/extensions/git/build/update-emoji.js
Normal file
@ -0,0 +1,101 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const path = require('path');
|
||||
|
||||
async function generate() {
|
||||
/**
|
||||
* @type {Map<string, string>}
|
||||
*/
|
||||
const shortcodeMap = new Map();
|
||||
|
||||
// Get emoji data from https://github.com/milesj/emojibase
|
||||
// https://github.com/milesj/emojibase/
|
||||
|
||||
const files = ['github.raw.json'] //, 'emojibase.raw.json']; //, 'iamcal.raw.json', 'joypixels.raw.json'];
|
||||
|
||||
for (const file of files) {
|
||||
await download(
|
||||
`https://raw.githubusercontent.com/milesj/emojibase/master/packages/data/en/shortcodes/${file}`,
|
||||
file,
|
||||
);
|
||||
|
||||
/**
|
||||
* @type {Record<string, string | string[]>}}
|
||||
*/
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const data = require(path.join(process.cwd(), file));
|
||||
for (const [emojis, codes] of Object.entries(data)) {
|
||||
const emoji = emojis
|
||||
.split('-')
|
||||
.map(c => String.fromCodePoint(parseInt(c, 16)))
|
||||
.join('');
|
||||
for (const code of Array.isArray(codes) ? codes : [codes]) {
|
||||
if (shortcodeMap.has(code)) {
|
||||
// console.warn(`${file}: ${code}`);
|
||||
continue;
|
||||
}
|
||||
shortcodeMap.set(code, emoji);
|
||||
}
|
||||
}
|
||||
|
||||
fs.unlink(file, () => { });
|
||||
}
|
||||
|
||||
// Get gitmoji data from https://github.com/carloscuesta/gitmoji
|
||||
// https://github.com/carloscuesta/gitmoji/blob/master/src/data/gitmojis.json
|
||||
await download(
|
||||
'https://raw.githubusercontent.com/carloscuesta/gitmoji/master/src/data/gitmojis.json',
|
||||
'gitmojis.json',
|
||||
);
|
||||
|
||||
/**
|
||||
* @type {({ code: string; emoji: string })[]}
|
||||
*/
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const gitmojis = require(path.join(process.cwd(), 'gitmojis.json')).gitmojis;
|
||||
for (const emoji of gitmojis) {
|
||||
if (emoji.code.startsWith(':') && emoji.code.endsWith(':')) {
|
||||
emoji.code = emoji.code.substring(1, emoji.code.length - 2);
|
||||
}
|
||||
|
||||
if (shortcodeMap.has(emoji.code)) {
|
||||
// console.warn(`GitHub: ${emoji.code}`);
|
||||
continue;
|
||||
}
|
||||
shortcodeMap.set(emoji.code, emoji.emoji);
|
||||
}
|
||||
|
||||
fs.unlink('gitmojis.json', () => { });
|
||||
|
||||
// Sort the emojis for easier diff checking
|
||||
const list = [...shortcodeMap.entries()];
|
||||
list.sort();
|
||||
|
||||
const map = list.reduce((m, [key, value]) => {
|
||||
m[key] = value;
|
||||
return m;
|
||||
}, Object.create(null));
|
||||
|
||||
fs.writeFileSync(path.join(process.cwd(), 'resources/emojis.json'), JSON.stringify(map), 'utf8');
|
||||
}
|
||||
|
||||
function download(url, destination) {
|
||||
return new Promise(resolve => {
|
||||
const stream = fs.createWriteStream(destination);
|
||||
https.get(url, rsp => {
|
||||
rsp.pipe(stream);
|
||||
stream.on('finish', () => {
|
||||
stream.close();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void generate();
|
16
lib/vscode/extensions/git/build/update-grammars.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
var updateGrammar = require('../../../build/npm/update-grammar');
|
||||
|
||||
updateGrammar.update('textmate/git.tmbundle', 'Syntaxes/Git%20Commit%20Message.tmLanguage', './syntaxes/git-commit.tmLanguage.json');
|
||||
updateGrammar.update('textmate/git.tmbundle', 'Syntaxes/Git%20Rebase%20Message.tmLanguage', './syntaxes/git-rebase.tmLanguage.json');
|
||||
updateGrammar.update('textmate/diff.tmbundle', 'Syntaxes/Diff.plist', './syntaxes/diff.tmLanguage.json');
|
||||
|
||||
|
||||
|
||||
|
||||
|
66
lib/vscode/extensions/git/cgmanifest.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"registrations": [
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"name": "textmate/git.tmbundle",
|
||||
"repositoryUrl": "https://github.com/textmate/git.tmbundle",
|
||||
"commitHash": "5870cf3f8abad3a6637bdf69250b5d2ded427dc4"
|
||||
}
|
||||
},
|
||||
"licenseDetail": [
|
||||
"Copyright (c) 2008 Tim Harper",
|
||||
"",
|
||||
"Permission is hereby granted, free of charge, to any person obtaining",
|
||||
"a copy of this software and associated documentation files (the\"",
|
||||
"Software\"), to deal in the Software without restriction, including",
|
||||
"without limitation the rights to use, copy, modify, merge, publish,",
|
||||
"distribute, sublicense, and/or sell copies of the Software, and to",
|
||||
"permit persons to whom the Software is furnished to do so, subject to",
|
||||
"the following conditions:",
|
||||
"",
|
||||
"The above copyright notice and this permission notice shall be",
|
||||
"included in all copies or substantial portions of the Software.",
|
||||
"",
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,",
|
||||
"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF",
|
||||
"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND",
|
||||
"NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE",
|
||||
"LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION",
|
||||
"OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION",
|
||||
"WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
|
||||
],
|
||||
"license": "MIT",
|
||||
"version": "0.0.0"
|
||||
},
|
||||
{
|
||||
"component": {
|
||||
"type": "git",
|
||||
"git": {
|
||||
"name": "textmate/diff.tmbundle",
|
||||
"repositoryUrl": "https://github.com/textmate/diff.tmbundle",
|
||||
"commitHash": "0593bb775eab1824af97ef2172fd38822abd97d7"
|
||||
}
|
||||
},
|
||||
"licenseDetail": [
|
||||
"Copyright (c) textmate-diff.tmbundle project authors",
|
||||
"",
|
||||
"If not otherwise specified (see below), files in this repository fall under the following license:",
|
||||
"",
|
||||
"Permission to copy, use, modify, sell and distribute this",
|
||||
"software is granted. This software is provided \"as is\" without",
|
||||
"express or implied warranty, and with no claim as to its",
|
||||
"suitability for any purpose.",
|
||||
"",
|
||||
"An exception is made for files in readable text which contain their own license information,",
|
||||
"or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added",
|
||||
"to the base-name name of the original file, and an extension of txt, html, or similar. For example",
|
||||
"\"tidy\" is accompanied by \"tidy-license.txt\"."
|
||||
],
|
||||
"license": "TextMate Bundle License",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
18
lib/vscode/extensions/git/extension.webpack.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
const withDefaults = require('../shared.webpack.config');
|
||||
|
||||
module.exports = withDefaults({
|
||||
context: __dirname,
|
||||
entry: {
|
||||
main: './src/main.ts',
|
||||
['askpass-main']: './src/askpass-main.ts'
|
||||
}
|
||||
});
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "#",
|
||||
"blockComment": [ "#", " " ]
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
]
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "#",
|
||||
"blockComment": [ "#", " " ]
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
]
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "#",
|
||||
"blockComment": [ "#", " " ]
|
||||
},
|
||||
"brackets": [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"]
|
||||
]
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"comments": {
|
||||
"lineComment": "#",
|
||||
}
|
||||
}
|
2219
lib/vscode/extensions/git/package.json
Normal file
192
lib/vscode/extensions/git/package.nls.json
Normal file
@ -0,0 +1,192 @@
|
||||
{
|
||||
"displayName": "Git",
|
||||
"description": "Git SCM Integration",
|
||||
"command.setLogLevel": "Set Log Level...",
|
||||
"command.clone": "Clone",
|
||||
"command.cloneRecursive": "Clone (Recursive)",
|
||||
"command.init": "Initialize Repository",
|
||||
"command.openRepository": "Open Repository",
|
||||
"command.close": "Close Repository",
|
||||
"command.refresh": "Refresh",
|
||||
"command.openChange": "Open Changes",
|
||||
"command.openFile": "Open File",
|
||||
"command.openHEADFile": "Open File (HEAD)",
|
||||
"command.stage": "Stage Changes",
|
||||
"command.stageAll": "Stage All Changes",
|
||||
"command.stageAllTracked": "Stage All Tracked Changes",
|
||||
"command.stageAllUntracked": "Stage All Untracked Changes",
|
||||
"command.stageAllMerge": "Stage All Merge Changes",
|
||||
"command.stageSelectedRanges": "Stage Selected Ranges",
|
||||
"command.revertSelectedRanges": "Revert Selected Ranges",
|
||||
"command.stageChange": "Stage Change",
|
||||
"command.revertChange": "Revert Change",
|
||||
"command.unstage": "Unstage Changes",
|
||||
"command.unstageAll": "Unstage All Changes",
|
||||
"command.unstageSelectedRanges": "Unstage Selected Ranges",
|
||||
"command.clean": "Discard Changes",
|
||||
"command.cleanAll": "Discard All Changes",
|
||||
"command.cleanAllTracked": "Discard All Tracked Changes",
|
||||
"command.cleanAllUntracked": "Discard All Untracked Changes",
|
||||
"command.commit": "Commit",
|
||||
"command.commitStaged": "Commit Staged",
|
||||
"command.commitEmpty": "Commit Empty",
|
||||
"command.commitStagedSigned": "Commit Staged (Signed Off)",
|
||||
"command.commitStagedAmend": "Commit Staged (Amend)",
|
||||
"command.commitAll": "Commit All",
|
||||
"command.commitAllSigned": "Commit All (Signed Off)",
|
||||
"command.commitAllAmend": "Commit All (Amend)",
|
||||
"command.commitNoVerify": "Commit (No Verify)",
|
||||
"command.commitStagedNoVerify": "Commit Staged (No Verify)",
|
||||
"command.commitEmptyNoVerify": "Commit Empty (No Verify)",
|
||||
"command.commitStagedSignedNoVerify": "Commit Staged (Signed Off, No Verify)",
|
||||
"command.commitStagedAmendNoVerify": "Commit Staged (Amend, No Verify)",
|
||||
"command.commitAllNoVerify": "Commit All (No Verify)",
|
||||
"command.commitAllSignedNoVerify": "Commit All (Signed Off, No Verify)",
|
||||
"command.commitAllAmendNoVerify": "Commit All (Amend, No Verify)",
|
||||
"command.restoreCommitTemplate": "Restore Commit Template",
|
||||
"command.undoCommit": "Undo Last Commit",
|
||||
"command.checkout": "Checkout to...",
|
||||
"command.branch": "Create Branch...",
|
||||
"command.branchFrom": "Create Branch From...",
|
||||
"command.deleteBranch": "Delete Branch...",
|
||||
"command.renameBranch": "Rename Branch...",
|
||||
"command.merge": "Merge Branch...",
|
||||
"command.rebase": "Rebase Branch...",
|
||||
"command.createTag": "Create Tag",
|
||||
"command.deleteTag": "Delete Tag",
|
||||
"command.fetch": "Fetch",
|
||||
"command.fetchPrune": "Fetch (Prune)",
|
||||
"command.fetchAll": "Fetch From All Remotes",
|
||||
"command.pull": "Pull",
|
||||
"command.pullRebase": "Pull (Rebase)",
|
||||
"command.pullFrom": "Pull from...",
|
||||
"command.push": "Push",
|
||||
"command.pushForce": "Push (Force)",
|
||||
"command.pushTo": "Push to...",
|
||||
"command.pushToForce": "Push to... (Force)",
|
||||
"command.pushFollowTags": "Push (Follow Tags)",
|
||||
"command.pushFollowTagsForce": "Push (Follow Tags, Force)",
|
||||
"command.addRemote": "Add Remote...",
|
||||
"command.removeRemote": "Remove Remote",
|
||||
"command.sync": "Sync",
|
||||
"command.syncRebase": "Sync (Rebase)",
|
||||
"command.publish": "Publish Branch...",
|
||||
"command.showOutput": "Show Git Output",
|
||||
"command.ignore": "Add to .gitignore",
|
||||
"command.revealInExplorer": "Reveal in Side Bar",
|
||||
"command.rebaseAbort": "Abort Rebase",
|
||||
"command.stashIncludeUntracked": "Stash (Include Untracked)",
|
||||
"command.stash": "Stash",
|
||||
"command.stashPop": "Pop Stash...",
|
||||
"command.stashPopLatest": "Pop Latest Stash",
|
||||
"command.stashApply": "Apply Stash...",
|
||||
"command.stashApplyLatest": "Apply Latest Stash",
|
||||
"command.stashDrop": "Drop Stash...",
|
||||
"command.timelineOpenDiff": "Open Changes",
|
||||
"command.timelineCopyCommitId": "Copy Commit ID",
|
||||
"command.timelineCopyCommitMessage": "Copy Commit Message",
|
||||
"config.enabled": "Whether git is enabled.",
|
||||
"config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows). This can also be an array of string values containing multiple paths to look up.",
|
||||
"config.autoRepositoryDetection": "Configures when repositories should be automatically detected.",
|
||||
"config.autoRepositoryDetection.true": "Scan for both subfolders of the current opened folder and parent folders of open files.",
|
||||
"config.autoRepositoryDetection.false": "Disable automatic repository scanning.",
|
||||
"config.autoRepositoryDetection.subFolders": "Scan for subfolders of the currently opened folder.",
|
||||
"config.autoRepositoryDetection.openEditors": "Scan for parent folders of open files.",
|
||||
"config.autorefresh": "Whether auto refreshing is enabled.",
|
||||
"config.autofetch": "When enabled, commits will automatically be fetched from the default remote of the current Git repository.",
|
||||
"config.autofetchPeriod": "Duration in seconds between each automatic git fetch, when `git.autofetch` is enabled.",
|
||||
"config.confirmSync": "Confirm before synchronizing git repositories.",
|
||||
"config.countBadge": "Controls the Git count badge.",
|
||||
"config.countBadge.all": "Count all changes.",
|
||||
"config.countBadge.tracked": "Count only tracked changes.",
|
||||
"config.countBadge.off": "Turn off counter.",
|
||||
"config.checkoutType": "Controls what type of branches are listed when running `Checkout to...`.",
|
||||
"config.checkoutType.all": "Show all references.",
|
||||
"config.checkoutType.local": "Show only local branches.",
|
||||
"config.checkoutType.tags": "Show only tags.",
|
||||
"config.checkoutType.remote": "Show only remote branches.",
|
||||
"config.branchValidationRegex": "A regular expression to validate new branch names.",
|
||||
"config.branchWhitespaceChar": "The character to replace whitespace in new branch names.",
|
||||
"config.ignoreLegacyWarning": "Ignores the legacy Git warning.",
|
||||
"config.ignoreMissingGitWarning": "Ignores the warning when Git is missing.",
|
||||
"config.ignoreWindowsGit27Warning": "Ignores the warning when Git 2.25 - 2.26 is installed on Windows.",
|
||||
"config.ignoreLimitWarning": "Ignores the warning when there are too many changes in a repository.",
|
||||
"config.defaultCloneDirectory": "The default location to clone a git repository.",
|
||||
"config.enableSmartCommit": "Commit all changes when there are no staged changes.",
|
||||
"config.smartCommitChanges": "Control which changes are automatically staged by Smart Commit.",
|
||||
"config.smartCommitChanges.all": "Automatically stage all changes.",
|
||||
"config.smartCommitChanges.tracked": "Automatically stage tracked changes only.",
|
||||
"config.suggestSmartCommit": "Suggests to enable smart commit (commit all changes when there are no staged changes).",
|
||||
"config.enableCommitSigning": "Enables commit signing with GPG or X.509.",
|
||||
"config.discardAllScope": "Controls what changes are discarded by the `Discard all changes` command. `all` discards all changes. `tracked` discards only tracked files. `prompt` shows a prompt dialog every time the action is run.",
|
||||
"config.decorations.enabled": "Controls whether Git contributes colors and badges to the explorer and the open editors view.",
|
||||
"config.enableStatusBarSync": "Controls whether the Git Sync command appears in the status bar.",
|
||||
"config.promptToSaveFilesBeforeCommit": "Controls whether Git should check for unsaved files before committing.",
|
||||
"config.promptToSaveFilesBeforeCommit.always": "Check for any unsaved files.",
|
||||
"config.promptToSaveFilesBeforeCommit.staged": "Check only for unsaved staged files.",
|
||||
"config.promptToSaveFilesBeforeCommit.never": "Disable this check.",
|
||||
"config.postCommitCommand": "Runs a git command after a successful commit.",
|
||||
"config.postCommitCommand.none": "Don't run any command after a commit.",
|
||||
"config.postCommitCommand.push": "Run 'Git Push' after a successful commit.",
|
||||
"config.postCommitCommand.sync": "Run 'Git Sync' after a successful commit.",
|
||||
"config.showInlineOpenFileAction": "Controls whether to show an inline Open File action in the Git changes view.",
|
||||
"config.showPushSuccessNotification": "Controls whether to show a notification when a push is successful.",
|
||||
"config.inputValidation": "Controls when to show commit message input validation.",
|
||||
"config.inputValidationLength": "Controls the commit message length threshold for showing a warning.",
|
||||
"config.inputValidationSubjectLength": "Controls the commit message subject length threshold for showing a warning. Unset it to inherit the value of `config.inputValidationLength`.",
|
||||
"config.detectSubmodules": "Controls whether to automatically detect git submodules.",
|
||||
"config.detectSubmodulesLimit": "Controls the limit of git submodules detected.",
|
||||
"config.alwaysShowStagedChangesResourceGroup": "Always show the Staged Changes resource group.",
|
||||
"config.alwaysSignOff": "Controls the signoff flag for all commits.",
|
||||
"config.ignoredRepositories": "List of git repositories to ignore.",
|
||||
"config.scanRepositories": "List of paths to search for git repositories in.",
|
||||
"config.showProgress": "Controls whether git actions should show progress.",
|
||||
"config.rebaseWhenSync": "Force git to use rebase when running the sync command.",
|
||||
"config.confirmEmptyCommits": "Always confirm the creation of empty commits for the 'Git: Commit Empty' command.",
|
||||
"config.fetchOnPull": "When enabled, fetch all branches when pulling. Otherwise, fetch just the current one.",
|
||||
"config.pullTags": "Fetch all tags when pulling.",
|
||||
"config.autoStash": "Stash any changes before pulling and restore them after successful pull.",
|
||||
"config.allowForcePush": "Controls whether force push (with or without lease) is enabled.",
|
||||
"config.useForcePushWithLease": "Controls whether force pushing uses the safer force-with-lease variant.",
|
||||
"config.confirmForcePush": "Controls whether to ask for confirmation before force-pushing.",
|
||||
"config.allowNoVerifyCommit": "Controls whether commits without running pre-commit and commit-msg hooks are allowed.",
|
||||
"config.confirmNoVerifyCommit": "Controls whether to ask for confirmation before commiting without verification.",
|
||||
"config.openDiffOnClick": "Controls whether the diff editor should be opened when clicking a change. Otherwise the regular editor will be opened.",
|
||||
"config.supportCancellation": "Controls whether a notification comes up when running the Sync action, which allows the user to cancel the operation.",
|
||||
"config.branchSortOrder": "Controls the sort order for branches.",
|
||||
"config.untrackedChanges": "Controls how untracked changes behave.",
|
||||
"config.untrackedChanges.mixed": "All changes, tracked and untracked, appear together and behave equally.",
|
||||
"config.untrackedChanges.separate": "Untracked changes appear separately in the Source Control view. They are also excluded from several actions.",
|
||||
"config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.",
|
||||
"config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.",
|
||||
"config.terminalAuthentication": "Controls whether to enable VS Code to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.",
|
||||
"config.timeline.showAuthor": "Controls whether to show the commit author in the Timeline view",
|
||||
"config.timeline.date": "Controls which date to use for items in the Timeline view",
|
||||
"config.timeline.date.committed": "Use the committed date",
|
||||
"config.timeline.date.authored": "Use the authored date",
|
||||
"submenu.commit": "Commit",
|
||||
"submenu.commit.amend": "Amend",
|
||||
"submenu.commit.signoff": "Sign Off",
|
||||
"submenu.changes": "Changes",
|
||||
"submenu.pullpush": "Pull, Push",
|
||||
"submenu.branch": "Branch",
|
||||
"submenu.remotes": "Remote",
|
||||
"submenu.stash": "Stash",
|
||||
"submenu.tags": "Tags",
|
||||
"colors.added": "Color for added resources.",
|
||||
"colors.modified": "Color for modified resources.",
|
||||
"colors.stageModified": "Color for modified resources which have been staged.",
|
||||
"colors.stageDeleted": "Color for deleted resources which have been staged.",
|
||||
"colors.deleted": "Color for deleted resources.",
|
||||
"colors.untracked": "Color for untracked resources.",
|
||||
"colors.ignored": "Color for ignored resources.",
|
||||
"colors.conflict": "Color for resources with conflicts.",
|
||||
"colors.submodule": "Color for submodule resources.",
|
||||
"view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use git and source control in VS Code in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.",
|
||||
"view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone Repository](command:git.clone)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository. You can initialize a repository which will enable source control features powered by git.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories. You can initialize a repository on a folder which will enable source control features powered by git.\n[Initialize Repository](command:git.init)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.scm.emptyWorkspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Add Folder to Workspace](command:workbench.action.addRootFolder)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.cloneRepository": "You can also clone a repository from a URL. To learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).\n[Clone Repository](command:git.clone)"
|
||||
}
|
1
lib/vscode/extensions/git/resources/emojis.json
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#3c8746" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
A
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#7F4E7E" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#692C77" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#9E121D" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
D
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
I
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#1B80B2" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
M
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
R
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
U
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
BIN
lib/vscode/extensions/git/resources/icons/git.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#2d883e" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
A
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#9B4F96" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#682079" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
C
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#B9131A" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
D
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
I
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#007ACC" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
M
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
R
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
@ -0,0 +1,6 @@
|
||||
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
|
||||
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback";" fill="white">
|
||||
U
|
||||
</text>
|
||||
</svg>
|
After Width: | Height: | Size: 431 B |
364
lib/vscode/extensions/git/src/api/api1.ts
Normal file
@ -0,0 +1,364 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Model } from '../model';
|
||||
import { Repository as BaseRepository, Resource } from '../repository';
|
||||
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery, PushErrorHandler } from './git';
|
||||
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands } from 'vscode';
|
||||
import { mapEvent } from '../util';
|
||||
import { toGitUri } from '../uri';
|
||||
import { pickRemoteSource, PickRemoteSourceOptions } from '../remoteSource';
|
||||
import { GitExtensionImpl } from './extension';
|
||||
|
||||
class ApiInputBox implements InputBox {
|
||||
set value(value: string) { this._inputBox.value = value; }
|
||||
get value(): string { return this._inputBox.value; }
|
||||
constructor(private _inputBox: SourceControlInputBox) { }
|
||||
}
|
||||
|
||||
export class ApiChange implements Change {
|
||||
|
||||
get uri(): Uri { return this.resource.resourceUri; }
|
||||
get originalUri(): Uri { return this.resource.original; }
|
||||
get renameUri(): Uri | undefined { return this.resource.renameResourceUri; }
|
||||
get status(): Status { return this.resource.type; }
|
||||
|
||||
constructor(private readonly resource: Resource) { }
|
||||
}
|
||||
|
||||
export class ApiRepositoryState implements RepositoryState {
|
||||
|
||||
get HEAD(): Branch | undefined { return this._repository.HEAD; }
|
||||
get refs(): Ref[] { return [...this._repository.refs]; }
|
||||
get remotes(): Remote[] { return [...this._repository.remotes]; }
|
||||
get submodules(): Submodule[] { return [...this._repository.submodules]; }
|
||||
get rebaseCommit(): Commit | undefined { return this._repository.rebaseCommit; }
|
||||
|
||||
get mergeChanges(): Change[] { return this._repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); }
|
||||
get indexChanges(): Change[] { return this._repository.indexGroup.resourceStates.map(r => new ApiChange(r)); }
|
||||
get workingTreeChanges(): Change[] { return this._repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); }
|
||||
|
||||
readonly onDidChange: Event<void> = this._repository.onDidRunGitStatus;
|
||||
|
||||
constructor(private _repository: BaseRepository) { }
|
||||
}
|
||||
|
||||
export class ApiRepositoryUIState implements RepositoryUIState {
|
||||
|
||||
get selected(): boolean { return this._sourceControl.selected; }
|
||||
|
||||
readonly onDidChange: Event<void> = mapEvent<boolean, void>(this._sourceControl.onDidChangeSelection, () => null);
|
||||
|
||||
constructor(private _sourceControl: SourceControl) { }
|
||||
}
|
||||
|
||||
export class ApiRepository implements Repository {
|
||||
|
||||
readonly rootUri: Uri = Uri.file(this._repository.root);
|
||||
readonly inputBox: InputBox = new ApiInputBox(this._repository.inputBox);
|
||||
readonly state: RepositoryState = new ApiRepositoryState(this._repository);
|
||||
readonly ui: RepositoryUIState = new ApiRepositoryUIState(this._repository.sourceControl);
|
||||
|
||||
constructor(private _repository: BaseRepository) { }
|
||||
|
||||
apply(patch: string, reverse?: boolean): Promise<void> {
|
||||
return this._repository.apply(patch, reverse);
|
||||
}
|
||||
|
||||
getConfigs(): Promise<{ key: string; value: string; }[]> {
|
||||
return this._repository.getConfigs();
|
||||
}
|
||||
|
||||
getConfig(key: string): Promise<string> {
|
||||
return this._repository.getConfig(key);
|
||||
}
|
||||
|
||||
setConfig(key: string, value: string): Promise<string> {
|
||||
return this._repository.setConfig(key, value);
|
||||
}
|
||||
|
||||
getGlobalConfig(key: string): Promise<string> {
|
||||
return this._repository.getGlobalConfig(key);
|
||||
}
|
||||
|
||||
getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> {
|
||||
return this._repository.getObjectDetails(treeish, path);
|
||||
}
|
||||
|
||||
detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> {
|
||||
return this._repository.detectObjectType(object);
|
||||
}
|
||||
|
||||
buffer(ref: string, filePath: string): Promise<Buffer> {
|
||||
return this._repository.buffer(ref, filePath);
|
||||
}
|
||||
|
||||
show(ref: string, path: string): Promise<string> {
|
||||
return this._repository.show(ref, path);
|
||||
}
|
||||
|
||||
getCommit(ref: string): Promise<Commit> {
|
||||
return this._repository.getCommit(ref);
|
||||
}
|
||||
|
||||
clean(paths: string[]) {
|
||||
return this._repository.clean(paths.map(p => Uri.file(p)));
|
||||
}
|
||||
|
||||
diff(cached?: boolean) {
|
||||
return this._repository.diff(cached);
|
||||
}
|
||||
|
||||
diffWithHEAD(): Promise<Change[]>;
|
||||
diffWithHEAD(path: string): Promise<string>;
|
||||
diffWithHEAD(path?: string): Promise<string | Change[]> {
|
||||
return this._repository.diffWithHEAD(path);
|
||||
}
|
||||
|
||||
diffWith(ref: string): Promise<Change[]>;
|
||||
diffWith(ref: string, path: string): Promise<string>;
|
||||
diffWith(ref: string, path?: string): Promise<string | Change[]> {
|
||||
return this._repository.diffWith(ref, path);
|
||||
}
|
||||
|
||||
diffIndexWithHEAD(): Promise<Change[]>;
|
||||
diffIndexWithHEAD(path: string): Promise<string>;
|
||||
diffIndexWithHEAD(path?: string): Promise<string | Change[]> {
|
||||
return this._repository.diffIndexWithHEAD(path);
|
||||
}
|
||||
|
||||
diffIndexWith(ref: string): Promise<Change[]>;
|
||||
diffIndexWith(ref: string, path: string): Promise<string>;
|
||||
diffIndexWith(ref: string, path?: string): Promise<string | Change[]> {
|
||||
return this._repository.diffIndexWith(ref, path);
|
||||
}
|
||||
|
||||
diffBlobs(object1: string, object2: string): Promise<string> {
|
||||
return this._repository.diffBlobs(object1, object2);
|
||||
}
|
||||
|
||||
diffBetween(ref1: string, ref2: string): Promise<Change[]>;
|
||||
diffBetween(ref1: string, ref2: string, path: string): Promise<string>;
|
||||
diffBetween(ref1: string, ref2: string, path?: string): Promise<string | Change[]> {
|
||||
return this._repository.diffBetween(ref1, ref2, path);
|
||||
}
|
||||
|
||||
hashObject(data: string): Promise<string> {
|
||||
return this._repository.hashObject(data);
|
||||
}
|
||||
|
||||
createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise<void> {
|
||||
return this._repository.branch(name, checkout, ref);
|
||||
}
|
||||
|
||||
deleteBranch(name: string, force?: boolean): Promise<void> {
|
||||
return this._repository.deleteBranch(name, force);
|
||||
}
|
||||
|
||||
getBranch(name: string): Promise<Branch> {
|
||||
return this._repository.getBranch(name);
|
||||
}
|
||||
|
||||
getBranches(query: BranchQuery): Promise<Ref[]> {
|
||||
return this._repository.getBranches(query);
|
||||
}
|
||||
|
||||
setBranchUpstream(name: string, upstream: string): Promise<void> {
|
||||
return this._repository.setBranchUpstream(name, upstream);
|
||||
}
|
||||
|
||||
getMergeBase(ref1: string, ref2: string): Promise<string> {
|
||||
return this._repository.getMergeBase(ref1, ref2);
|
||||
}
|
||||
|
||||
status(): Promise<void> {
|
||||
return this._repository.status();
|
||||
}
|
||||
|
||||
checkout(treeish: string): Promise<void> {
|
||||
return this._repository.checkout(treeish);
|
||||
}
|
||||
|
||||
addRemote(name: string, url: string): Promise<void> {
|
||||
return this._repository.addRemote(name, url);
|
||||
}
|
||||
|
||||
removeRemote(name: string): Promise<void> {
|
||||
return this._repository.removeRemote(name);
|
||||
}
|
||||
|
||||
renameRemote(name: string, newName: string): Promise<void> {
|
||||
return this._repository.renameRemote(name, newName);
|
||||
}
|
||||
|
||||
fetch(remote?: string | undefined, ref?: string | undefined, depth?: number | undefined): Promise<void> {
|
||||
return this._repository.fetch(remote, ref, depth);
|
||||
}
|
||||
|
||||
pull(unshallow?: boolean): Promise<void> {
|
||||
return this._repository.pull(undefined, unshallow);
|
||||
}
|
||||
|
||||
push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise<void> {
|
||||
return this._repository.pushTo(remoteName, branchName, setUpstream);
|
||||
}
|
||||
|
||||
blame(path: string): Promise<string> {
|
||||
return this._repository.blame(path);
|
||||
}
|
||||
|
||||
log(options?: LogOptions): Promise<Commit[]> {
|
||||
return this._repository.log(options);
|
||||
}
|
||||
|
||||
commit(message: string, opts?: CommitOptions): Promise<void> {
|
||||
return this._repository.commit(message, opts);
|
||||
}
|
||||
}
|
||||
|
||||
export class ApiGit implements Git {
|
||||
|
||||
get path(): string { return this._model.git.path; }
|
||||
|
||||
constructor(private _model: Model) { }
|
||||
}
|
||||
|
||||
export class ApiImpl implements API {
|
||||
|
||||
readonly git = new ApiGit(this._model);
|
||||
|
||||
get state(): APIState {
|
||||
return this._model.state;
|
||||
}
|
||||
|
||||
get onDidChangeState(): Event<APIState> {
|
||||
return this._model.onDidChangeState;
|
||||
}
|
||||
|
||||
get onDidOpenRepository(): Event<Repository> {
|
||||
return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r));
|
||||
}
|
||||
|
||||
get onDidCloseRepository(): Event<Repository> {
|
||||
return mapEvent(this._model.onDidCloseRepository, r => new ApiRepository(r));
|
||||
}
|
||||
|
||||
get repositories(): Repository[] {
|
||||
return this._model.repositories.map(r => new ApiRepository(r));
|
||||
}
|
||||
|
||||
toGitUri(uri: Uri, ref: string): Uri {
|
||||
return toGitUri(uri, ref);
|
||||
}
|
||||
|
||||
getRepository(uri: Uri): Repository | null {
|
||||
const result = this._model.getRepository(uri);
|
||||
return result ? new ApiRepository(result) : null;
|
||||
}
|
||||
|
||||
async init(root: Uri): Promise<Repository | null> {
|
||||
const path = root.fsPath;
|
||||
await this._model.git.init(path);
|
||||
await this._model.openRepository(path);
|
||||
return this.getRepository(root) || null;
|
||||
}
|
||||
|
||||
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
|
||||
return this._model.registerRemoteSourceProvider(provider);
|
||||
}
|
||||
|
||||
registerCredentialsProvider(provider: CredentialsProvider): Disposable {
|
||||
return this._model.registerCredentialsProvider(provider);
|
||||
}
|
||||
|
||||
registerPushErrorHandler(handler: PushErrorHandler): Disposable {
|
||||
return this._model.registerPushErrorHandler(handler);
|
||||
}
|
||||
|
||||
constructor(private _model: Model) { }
|
||||
}
|
||||
|
||||
function getRefType(type: RefType): string {
|
||||
switch (type) {
|
||||
case RefType.Head: return 'Head';
|
||||
case RefType.RemoteHead: return 'RemoteHead';
|
||||
case RefType.Tag: return 'Tag';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function getStatus(status: Status): string {
|
||||
switch (status) {
|
||||
case Status.INDEX_MODIFIED: return 'INDEX_MODIFIED';
|
||||
case Status.INDEX_ADDED: return 'INDEX_ADDED';
|
||||
case Status.INDEX_DELETED: return 'INDEX_DELETED';
|
||||
case Status.INDEX_RENAMED: return 'INDEX_RENAMED';
|
||||
case Status.INDEX_COPIED: return 'INDEX_COPIED';
|
||||
case Status.MODIFIED: return 'MODIFIED';
|
||||
case Status.DELETED: return 'DELETED';
|
||||
case Status.UNTRACKED: return 'UNTRACKED';
|
||||
case Status.IGNORED: return 'IGNORED';
|
||||
case Status.INTENT_TO_ADD: return 'INTENT_TO_ADD';
|
||||
case Status.ADDED_BY_US: return 'ADDED_BY_US';
|
||||
case Status.ADDED_BY_THEM: return 'ADDED_BY_THEM';
|
||||
case Status.DELETED_BY_US: return 'DELETED_BY_US';
|
||||
case Status.DELETED_BY_THEM: return 'DELETED_BY_THEM';
|
||||
case Status.BOTH_ADDED: return 'BOTH_ADDED';
|
||||
case Status.BOTH_DELETED: return 'BOTH_DELETED';
|
||||
case Status.BOTH_MODIFIED: return 'BOTH_MODIFIED';
|
||||
}
|
||||
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
|
||||
export function registerAPICommands(extension: GitExtensionImpl): Disposable {
|
||||
const disposables: Disposable[] = [];
|
||||
|
||||
disposables.push(commands.registerCommand('git.api.getRepositories', () => {
|
||||
const api = extension.getAPI(1);
|
||||
return api.repositories.map(r => r.rootUri.toString());
|
||||
}));
|
||||
|
||||
disposables.push(commands.registerCommand('git.api.getRepositoryState', (uri: string) => {
|
||||
const api = extension.getAPI(1);
|
||||
const repository = api.getRepository(Uri.parse(uri));
|
||||
|
||||
if (!repository) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const state = repository.state;
|
||||
|
||||
const ref = (ref: Ref | undefined) => (ref && { ...ref, type: getRefType(ref.type) });
|
||||
const change = (change: Change) => ({
|
||||
uri: change.uri.toString(),
|
||||
originalUri: change.originalUri.toString(),
|
||||
renameUri: change.renameUri?.toString(),
|
||||
status: getStatus(change.status)
|
||||
});
|
||||
|
||||
return {
|
||||
HEAD: ref(state.HEAD),
|
||||
refs: state.refs.map(ref),
|
||||
remotes: state.remotes,
|
||||
submodules: state.submodules,
|
||||
rebaseCommit: state.rebaseCommit,
|
||||
mergeChanges: state.mergeChanges.map(change),
|
||||
indexChanges: state.indexChanges.map(change),
|
||||
workingTreeChanges: state.workingTreeChanges.map(change)
|
||||
};
|
||||
}));
|
||||
|
||||
disposables.push(commands.registerCommand('git.api.getRemoteSources', (opts?: PickRemoteSourceOptions) => {
|
||||
if (!extension.model) {
|
||||
return;
|
||||
}
|
||||
|
||||
return pickRemoteSource(extension.model, opts);
|
||||
}));
|
||||
|
||||
return Disposable.from(...disposables);
|
||||
}
|
85
lib/vscode/extensions/git/src/api/extension.ts
Normal file
@ -0,0 +1,85 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Model } from '../model';
|
||||
import { GitExtension, Repository, API } from './git';
|
||||
import { ApiRepository, ApiImpl } from './api1';
|
||||
import { Event, EventEmitter } from 'vscode';
|
||||
|
||||
export function deprecated(_target: any, key: string, descriptor: any): void {
|
||||
if (typeof descriptor.value !== 'function') {
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
const fn = descriptor.value;
|
||||
descriptor.value = function () {
|
||||
console.warn(`Git extension API method '${key}' is deprecated.`);
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
export class GitExtensionImpl implements GitExtension {
|
||||
|
||||
enabled: boolean = false;
|
||||
|
||||
private _onDidChangeEnablement = new EventEmitter<boolean>();
|
||||
readonly onDidChangeEnablement: Event<boolean> = this._onDidChangeEnablement.event;
|
||||
|
||||
private _model: Model | undefined = undefined;
|
||||
|
||||
set model(model: Model | undefined) {
|
||||
this._model = model;
|
||||
|
||||
const enabled = !!model;
|
||||
|
||||
if (this.enabled === enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = enabled;
|
||||
this._onDidChangeEnablement.fire(this.enabled);
|
||||
}
|
||||
|
||||
get model(): Model | undefined {
|
||||
return this._model;
|
||||
}
|
||||
|
||||
constructor(model?: Model) {
|
||||
if (model) {
|
||||
this.enabled = true;
|
||||
this._model = model;
|
||||
}
|
||||
}
|
||||
|
||||
@deprecated
|
||||
async getGitPath(): Promise<string> {
|
||||
if (!this._model) {
|
||||
throw new Error('Git model not found');
|
||||
}
|
||||
|
||||
return this._model.git.path;
|
||||
}
|
||||
|
||||
@deprecated
|
||||
async getRepositories(): Promise<Repository[]> {
|
||||
if (!this._model) {
|
||||
throw new Error('Git model not found');
|
||||
}
|
||||
|
||||
return this._model.repositories.map(repository => new ApiRepository(repository));
|
||||
}
|
||||
|
||||
getAPI(version: number): API {
|
||||
if (!this._model) {
|
||||
throw new Error('Git model not found');
|
||||
}
|
||||
|
||||
if (version !== 1) {
|
||||
throw new Error(`No API version ${version} found.`);
|
||||
}
|
||||
|
||||
return new ApiImpl(this._model);
|
||||
}
|
||||
}
|
304
lib/vscode/extensions/git/src/api/git.d.ts
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Uri, Event, Disposable, ProviderResult } from 'vscode';
|
||||
export { ProviderResult } from 'vscode';
|
||||
|
||||
export interface Git {
|
||||
readonly path: string;
|
||||
}
|
||||
|
||||
export interface InputBox {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export const enum RefType {
|
||||
Head,
|
||||
RemoteHead,
|
||||
Tag
|
||||
}
|
||||
|
||||
export interface Ref {
|
||||
readonly type: RefType;
|
||||
readonly name?: string;
|
||||
readonly commit?: string;
|
||||
readonly remote?: string;
|
||||
}
|
||||
|
||||
export interface UpstreamRef {
|
||||
readonly remote: string;
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
export interface Branch extends Ref {
|
||||
readonly upstream?: UpstreamRef;
|
||||
readonly ahead?: number;
|
||||
readonly behind?: number;
|
||||
}
|
||||
|
||||
export interface Commit {
|
||||
readonly hash: string;
|
||||
readonly message: string;
|
||||
readonly parents: string[];
|
||||
readonly authorDate?: Date;
|
||||
readonly authorName?: string;
|
||||
readonly authorEmail?: string;
|
||||
readonly commitDate?: Date;
|
||||
}
|
||||
|
||||
export interface Submodule {
|
||||
readonly name: string;
|
||||
readonly path: string;
|
||||
readonly url: string;
|
||||
}
|
||||
|
||||
export interface Remote {
|
||||
readonly name: string;
|
||||
readonly fetchUrl?: string;
|
||||
readonly pushUrl?: string;
|
||||
readonly isReadOnly: boolean;
|
||||
}
|
||||
|
||||
export const enum Status {
|
||||
INDEX_MODIFIED,
|
||||
INDEX_ADDED,
|
||||
INDEX_DELETED,
|
||||
INDEX_RENAMED,
|
||||
INDEX_COPIED,
|
||||
|
||||
MODIFIED,
|
||||
DELETED,
|
||||
UNTRACKED,
|
||||
IGNORED,
|
||||
INTENT_TO_ADD,
|
||||
|
||||
ADDED_BY_US,
|
||||
ADDED_BY_THEM,
|
||||
DELETED_BY_US,
|
||||
DELETED_BY_THEM,
|
||||
BOTH_ADDED,
|
||||
BOTH_DELETED,
|
||||
BOTH_MODIFIED
|
||||
}
|
||||
|
||||
export interface Change {
|
||||
|
||||
/**
|
||||
* Returns either `originalUri` or `renameUri`, depending
|
||||
* on whether this change is a rename change. When
|
||||
* in doubt always use `uri` over the other two alternatives.
|
||||
*/
|
||||
readonly uri: Uri;
|
||||
readonly originalUri: Uri;
|
||||
readonly renameUri: Uri | undefined;
|
||||
readonly status: Status;
|
||||
}
|
||||
|
||||
export interface RepositoryState {
|
||||
readonly HEAD: Branch | undefined;
|
||||
readonly refs: Ref[];
|
||||
readonly remotes: Remote[];
|
||||
readonly submodules: Submodule[];
|
||||
readonly rebaseCommit: Commit | undefined;
|
||||
|
||||
readonly mergeChanges: Change[];
|
||||
readonly indexChanges: Change[];
|
||||
readonly workingTreeChanges: Change[];
|
||||
|
||||
readonly onDidChange: Event<void>;
|
||||
}
|
||||
|
||||
export interface RepositoryUIState {
|
||||
readonly selected: boolean;
|
||||
readonly onDidChange: Event<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log options.
|
||||
*/
|
||||
export interface LogOptions {
|
||||
/** Max number of log entries to retrieve. If not specified, the default is 32. */
|
||||
readonly maxEntries?: number;
|
||||
readonly path?: string;
|
||||
}
|
||||
|
||||
export interface CommitOptions {
|
||||
all?: boolean | 'tracked';
|
||||
amend?: boolean;
|
||||
signoff?: boolean;
|
||||
signCommit?: boolean;
|
||||
empty?: boolean;
|
||||
noVerify?: boolean;
|
||||
}
|
||||
|
||||
export interface BranchQuery {
|
||||
readonly remote?: boolean;
|
||||
readonly pattern?: string;
|
||||
readonly count?: number;
|
||||
readonly contains?: string;
|
||||
}
|
||||
|
||||
export interface Repository {
|
||||
|
||||
readonly rootUri: Uri;
|
||||
readonly inputBox: InputBox;
|
||||
readonly state: RepositoryState;
|
||||
readonly ui: RepositoryUIState;
|
||||
|
||||
getConfigs(): Promise<{ key: string; value: string; }[]>;
|
||||
getConfig(key: string): Promise<string>;
|
||||
setConfig(key: string, value: string): Promise<string>;
|
||||
getGlobalConfig(key: string): Promise<string>;
|
||||
|
||||
getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>;
|
||||
detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }>;
|
||||
buffer(ref: string, path: string): Promise<Buffer>;
|
||||
show(ref: string, path: string): Promise<string>;
|
||||
getCommit(ref: string): Promise<Commit>;
|
||||
|
||||
clean(paths: string[]): Promise<void>;
|
||||
|
||||
apply(patch: string, reverse?: boolean): Promise<void>;
|
||||
diff(cached?: boolean): Promise<string>;
|
||||
diffWithHEAD(): Promise<Change[]>;
|
||||
diffWithHEAD(path: string): Promise<string>;
|
||||
diffWith(ref: string): Promise<Change[]>;
|
||||
diffWith(ref: string, path: string): Promise<string>;
|
||||
diffIndexWithHEAD(): Promise<Change[]>;
|
||||
diffIndexWithHEAD(path: string): Promise<string>;
|
||||
diffIndexWith(ref: string): Promise<Change[]>;
|
||||
diffIndexWith(ref: string, path: string): Promise<string>;
|
||||
diffBlobs(object1: string, object2: string): Promise<string>;
|
||||
diffBetween(ref1: string, ref2: string): Promise<Change[]>;
|
||||
diffBetween(ref1: string, ref2: string, path: string): Promise<string>;
|
||||
|
||||
hashObject(data: string): Promise<string>;
|
||||
|
||||
createBranch(name: string, checkout: boolean, ref?: string): Promise<void>;
|
||||
deleteBranch(name: string, force?: boolean): Promise<void>;
|
||||
getBranch(name: string): Promise<Branch>;
|
||||
getBranches(query: BranchQuery): Promise<Ref[]>;
|
||||
setBranchUpstream(name: string, upstream: string): Promise<void>;
|
||||
|
||||
getMergeBase(ref1: string, ref2: string): Promise<string>;
|
||||
|
||||
status(): Promise<void>;
|
||||
checkout(treeish: string): Promise<void>;
|
||||
|
||||
addRemote(name: string, url: string): Promise<void>;
|
||||
removeRemote(name: string): Promise<void>;
|
||||
renameRemote(name: string, newName: string): Promise<void>;
|
||||
|
||||
fetch(remote?: string, ref?: string, depth?: number): Promise<void>;
|
||||
pull(unshallow?: boolean): Promise<void>;
|
||||
push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise<void>;
|
||||
|
||||
blame(path: string): Promise<string>;
|
||||
log(options?: LogOptions): Promise<Commit[]>;
|
||||
|
||||
commit(message: string, opts?: CommitOptions): Promise<void>;
|
||||
}
|
||||
|
||||
export interface RemoteSource {
|
||||
readonly name: string;
|
||||
readonly description?: string;
|
||||
readonly url: string | string[];
|
||||
}
|
||||
|
||||
export interface RemoteSourceProvider {
|
||||
readonly name: string;
|
||||
readonly icon?: string; // codicon name
|
||||
readonly supportsQuery?: boolean;
|
||||
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
|
||||
publishRepository?(repository: Repository): Promise<void>;
|
||||
}
|
||||
|
||||
export interface Credentials {
|
||||
readonly username: string;
|
||||
readonly password: string;
|
||||
}
|
||||
|
||||
export interface CredentialsProvider {
|
||||
getCredentials(host: Uri): ProviderResult<Credentials>;
|
||||
}
|
||||
|
||||
export interface PushErrorHandler {
|
||||
handlePushError(repository: Repository, remote: Remote, refspec: string, error: Error & { gitErrorCode: GitErrorCodes }): Promise<boolean>;
|
||||
}
|
||||
|
||||
export type APIState = 'uninitialized' | 'initialized';
|
||||
|
||||
export interface API {
|
||||
readonly state: APIState;
|
||||
readonly onDidChangeState: Event<APIState>;
|
||||
readonly git: Git;
|
||||
readonly repositories: Repository[];
|
||||
readonly onDidOpenRepository: Event<Repository>;
|
||||
readonly onDidCloseRepository: Event<Repository>;
|
||||
|
||||
toGitUri(uri: Uri, ref: string): Uri;
|
||||
getRepository(uri: Uri): Repository | null;
|
||||
init(root: Uri): Promise<Repository | null>;
|
||||
|
||||
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
|
||||
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
|
||||
registerPushErrorHandler(handler: PushErrorHandler): Disposable;
|
||||
}
|
||||
|
||||
export interface GitExtension {
|
||||
|
||||
readonly enabled: boolean;
|
||||
readonly onDidChangeEnablement: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Returns a specific API version.
|
||||
*
|
||||
* Throws error if git extension is disabled. You can listed to the
|
||||
* [GitExtension.onDidChangeEnablement](#GitExtension.onDidChangeEnablement) event
|
||||
* to know when the extension becomes enabled/disabled.
|
||||
*
|
||||
* @param version Version number.
|
||||
* @returns API instance
|
||||
*/
|
||||
getAPI(version: 1): API;
|
||||
}
|
||||
|
||||
export const enum GitErrorCodes {
|
||||
BadConfigFile = 'BadConfigFile',
|
||||
AuthenticationFailed = 'AuthenticationFailed',
|
||||
NoUserNameConfigured = 'NoUserNameConfigured',
|
||||
NoUserEmailConfigured = 'NoUserEmailConfigured',
|
||||
NoRemoteRepositorySpecified = 'NoRemoteRepositorySpecified',
|
||||
NotAGitRepository = 'NotAGitRepository',
|
||||
NotAtRepositoryRoot = 'NotAtRepositoryRoot',
|
||||
Conflict = 'Conflict',
|
||||
StashConflict = 'StashConflict',
|
||||
UnmergedChanges = 'UnmergedChanges',
|
||||
PushRejected = 'PushRejected',
|
||||
RemoteConnectionError = 'RemoteConnectionError',
|
||||
DirtyWorkTree = 'DirtyWorkTree',
|
||||
CantOpenResource = 'CantOpenResource',
|
||||
GitNotFound = 'GitNotFound',
|
||||
CantCreatePipe = 'CantCreatePipe',
|
||||
PermissionDenied = 'PermissionDenied',
|
||||
CantAccessRemote = 'CantAccessRemote',
|
||||
RepositoryNotFound = 'RepositoryNotFound',
|
||||
RepositoryIsLocked = 'RepositoryIsLocked',
|
||||
BranchNotFullyMerged = 'BranchNotFullyMerged',
|
||||
NoRemoteReference = 'NoRemoteReference',
|
||||
InvalidBranchName = 'InvalidBranchName',
|
||||
BranchAlreadyExists = 'BranchAlreadyExists',
|
||||
NoLocalChanges = 'NoLocalChanges',
|
||||
NoStashFound = 'NoStashFound',
|
||||
LocalChangesOverwritten = 'LocalChangesOverwritten',
|
||||
NoUpstreamBranch = 'NoUpstreamBranch',
|
||||
IsInSubmodule = 'IsInSubmodule',
|
||||
WrongCase = 'WrongCase',
|
||||
CantLockRef = 'CantLockRef',
|
||||
CantRebaseMultipleBranches = 'CantRebaseMultipleBranches',
|
||||
PatchDoesNotApply = 'PatchDoesNotApply',
|
||||
NoPathFound = 'NoPathFound',
|
||||
UnknownPath = 'UnknownPath',
|
||||
}
|
2
lib/vscode/extensions/git/src/askpass-empty.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
echo ''
|
42
lib/vscode/extensions/git/src/askpass-main.ts
Normal file
@ -0,0 +1,42 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { IPCClient } from './ipc/ipcClient';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
function fatal(err: any): void {
|
||||
console.error(localize('missOrInvalid', "Missing or invalid credentials."));
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function main(argv: string[]): void {
|
||||
if (argv.length !== 5) {
|
||||
return fatal('Wrong number of arguments');
|
||||
}
|
||||
|
||||
if (!process.env['VSCODE_GIT_ASKPASS_PIPE']) {
|
||||
return fatal('Missing pipe');
|
||||
}
|
||||
|
||||
if (process.env['VSCODE_GIT_COMMAND'] === 'fetch' && !!process.env['VSCODE_GIT_FETCH_SILENT']) {
|
||||
return fatal('Skip silent fetch commands');
|
||||
}
|
||||
|
||||
const output = process.env['VSCODE_GIT_ASKPASS_PIPE'] as string;
|
||||
const request = argv[2];
|
||||
const host = argv[4].substring(1, argv[4].length - 2);
|
||||
const ipcClient = new IPCClient('askpass');
|
||||
|
||||
ipcClient.call({ request, host }).then(res => {
|
||||
fs.writeFileSync(output, res + '\n');
|
||||
setTimeout(() => process.exit(0), 0);
|
||||
}).catch(err => fatal(err));
|
||||
}
|
||||
|
||||
main(process.argv);
|
5
lib/vscode/extensions/git/src/askpass.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
VSCODE_GIT_ASKPASS_PIPE=`mktemp`
|
||||
ELECTRON_RUN_AS_NODE="1" VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_MAIN" $*
|
||||
cat $VSCODE_GIT_ASKPASS_PIPE
|
||||
rm $VSCODE_GIT_ASKPASS_PIPE
|
98
lib/vscode/extensions/git/src/askpass.ts
Normal file
@ -0,0 +1,98 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { window, InputBoxOptions, Uri, OutputChannel, Disposable, workspace } from 'vscode';
|
||||
import { IDisposable, EmptyDisposable, toDisposable } from './util';
|
||||
import * as path from 'path';
|
||||
import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer';
|
||||
import { CredentialsProvider, Credentials } from './api/git';
|
||||
|
||||
export class Askpass implements IIPCHandler {
|
||||
|
||||
private disposable: IDisposable = EmptyDisposable;
|
||||
private cache = new Map<string, Credentials>();
|
||||
private credentialsProviders = new Set<CredentialsProvider>();
|
||||
|
||||
static async create(outputChannel: OutputChannel, context?: string): Promise<Askpass> {
|
||||
try {
|
||||
return new Askpass(await createIPCServer(context));
|
||||
} catch (err) {
|
||||
outputChannel.appendLine(`[error] Failed to create git askpass IPC: ${err}`);
|
||||
return new Askpass();
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(private ipc?: IIPCServer) {
|
||||
if (ipc) {
|
||||
this.disposable = ipc.registerHandler('askpass', this);
|
||||
}
|
||||
}
|
||||
|
||||
async handle({ request, host }: { request: string, host: string }): Promise<string> {
|
||||
const config = workspace.getConfiguration('git', null);
|
||||
const enabled = config.get<boolean>('enabled');
|
||||
|
||||
if (!enabled) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const uri = Uri.parse(host);
|
||||
const authority = uri.authority.replace(/^.*@/, '');
|
||||
const password = /password/i.test(request);
|
||||
const cached = this.cache.get(authority);
|
||||
|
||||
if (cached && password) {
|
||||
this.cache.delete(authority);
|
||||
return cached.password;
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
for (const credentialsProvider of this.credentialsProviders) {
|
||||
try {
|
||||
const credentials = await credentialsProvider.getCredentials(uri);
|
||||
|
||||
if (credentials) {
|
||||
this.cache.set(authority, credentials);
|
||||
setTimeout(() => this.cache.delete(authority), 60_000);
|
||||
return credentials.username;
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
|
||||
const options: InputBoxOptions = {
|
||||
password,
|
||||
placeHolder: request,
|
||||
prompt: `Git: ${host}`,
|
||||
ignoreFocusOut: true
|
||||
};
|
||||
|
||||
return await window.showInputBox(options) || '';
|
||||
}
|
||||
|
||||
getEnv(): { [key: string]: string; } {
|
||||
if (!this.ipc) {
|
||||
return {
|
||||
GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh')
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...this.ipc.getEnv(),
|
||||
GIT_ASKPASS: path.join(__dirname, 'askpass.sh'),
|
||||
VSCODE_GIT_ASKPASS_NODE: process.execPath,
|
||||
VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js')
|
||||
};
|
||||
}
|
||||
|
||||
registerCredentialsProvider(provider: CredentialsProvider): Disposable {
|
||||
this.credentialsProviders.add(provider);
|
||||
return toDisposable(() => this.credentialsProviders.delete(provider));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposable.dispose();
|
||||
}
|
||||
}
|
125
lib/vscode/extensions/git/src/autofetch.ts
Normal file
@ -0,0 +1,125 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { workspace, Disposable, EventEmitter, Memento, window, MessageItem, ConfigurationTarget, Uri } from 'vscode';
|
||||
import { Repository, Operation } from './repository';
|
||||
import { eventToPromise, filterEvent, onceEvent } from './util';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { GitErrorCodes } from './api/git';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
function isRemoteOperation(operation: Operation): boolean {
|
||||
return operation === Operation.Pull || operation === Operation.Push || operation === Operation.Sync || operation === Operation.Fetch;
|
||||
}
|
||||
|
||||
export class AutoFetcher {
|
||||
|
||||
private static DidInformUser = 'autofetch.didInformUser';
|
||||
|
||||
private _onDidChange = new EventEmitter<boolean>();
|
||||
private onDidChange = this._onDidChange.event;
|
||||
|
||||
private _enabled: boolean = false;
|
||||
get enabled(): boolean { return this._enabled; }
|
||||
set enabled(enabled: boolean) { this._enabled = enabled; this._onDidChange.fire(enabled); }
|
||||
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(private repository: Repository, private globalState: Memento) {
|
||||
workspace.onDidChangeConfiguration(this.onConfiguration, this, this.disposables);
|
||||
this.onConfiguration();
|
||||
|
||||
const onGoodRemoteOperation = filterEvent(repository.onDidRunOperation, ({ operation, error }) => !error && isRemoteOperation(operation));
|
||||
const onFirstGoodRemoteOperation = onceEvent(onGoodRemoteOperation);
|
||||
onFirstGoodRemoteOperation(this.onFirstGoodRemoteOperation, this, this.disposables);
|
||||
}
|
||||
|
||||
private async onFirstGoodRemoteOperation(): Promise<void> {
|
||||
const didInformUser = !this.globalState.get<boolean>(AutoFetcher.DidInformUser);
|
||||
|
||||
if (this.enabled && !didInformUser) {
|
||||
this.globalState.update(AutoFetcher.DidInformUser, true);
|
||||
}
|
||||
|
||||
const shouldInformUser = !this.enabled && didInformUser;
|
||||
|
||||
if (!shouldInformUser) {
|
||||
return;
|
||||
}
|
||||
|
||||
const yes: MessageItem = { title: localize('yes', "Yes") };
|
||||
const no: MessageItem = { isCloseAffordance: true, title: localize('no', "No") };
|
||||
const askLater: MessageItem = { title: localize('not now', "Ask Me Later") };
|
||||
const result = await window.showInformationMessage(localize('suggest auto fetch', "Would you like Code to [periodically run 'git fetch']({0})?", 'https://go.microsoft.com/fwlink/?linkid=865294'), yes, no, askLater);
|
||||
|
||||
if (result === askLater) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result === yes) {
|
||||
const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
|
||||
gitConfig.update('autofetch', true, ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
this.globalState.update(AutoFetcher.DidInformUser, true);
|
||||
}
|
||||
|
||||
private onConfiguration(): void {
|
||||
const gitConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
|
||||
|
||||
if (gitConfig.get<boolean>('autofetch') === false) {
|
||||
this.disable();
|
||||
} else {
|
||||
this.enable();
|
||||
}
|
||||
}
|
||||
|
||||
enable(): void {
|
||||
if (this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = true;
|
||||
this.run();
|
||||
}
|
||||
|
||||
disable(): void {
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
private async run(): Promise<void> {
|
||||
while (this.enabled) {
|
||||
await this.repository.whenIdleAndFocused();
|
||||
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.repository.fetchDefault({ silent: true });
|
||||
} catch (err) {
|
||||
if (err.gitErrorCode === GitErrorCodes.AuthenticationFailed) {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const period = workspace.getConfiguration('git', Uri.file(this.repository.root)).get<number>('autofetchPeriod', 180) * 1000;
|
||||
const timeout = new Promise(c => setTimeout(c, period));
|
||||
const whenDisabled = eventToPromise(filterEvent(this.onDidChange, enabled => !enabled));
|
||||
|
||||
await Promise.race([timeout, whenDisabled]);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disable();
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
}
|
||||
}
|
2759
lib/vscode/extensions/git/src/commands.ts
Normal file
207
lib/vscode/extensions/git/src/decorationProvider.ts
Normal file
@ -0,0 +1,207 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { window, workspace, Uri, Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, ThemeColor } from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { Repository, GitResourceGroup } from './repository';
|
||||
import { Model } from './model';
|
||||
import { debounce } from './decorators';
|
||||
import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource } from './util';
|
||||
import { GitErrorCodes, Status } from './api/git';
|
||||
|
||||
class GitIgnoreDecorationProvider implements FileDecorationProvider {
|
||||
|
||||
private static Decoration: FileDecoration = { color: new ThemeColor('gitDecoration.ignoredResourceForeground') };
|
||||
|
||||
readonly onDidChange: Event<Uri[]>;
|
||||
private queue = new Map<string, { repository: Repository; queue: Map<string, PromiseSource<FileDecoration | undefined>>; }>();
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(private model: Model) {
|
||||
this.onDidChange = fireEvent(anyEvent<any>(
|
||||
filterEvent(workspace.onDidSaveTextDocument, e => /\.gitignore$|\.git\/info\/exclude$/.test(e.uri.path)),
|
||||
model.onDidOpenRepository,
|
||||
model.onDidCloseRepository
|
||||
));
|
||||
|
||||
this.disposables.push(window.registerDecorationProvider(this));
|
||||
}
|
||||
|
||||
async provideFileDecoration(uri: Uri): Promise<FileDecoration | undefined> {
|
||||
const repository = this.model.getRepository(uri);
|
||||
|
||||
if (!repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
let queueItem = this.queue.get(repository.root);
|
||||
|
||||
if (!queueItem) {
|
||||
queueItem = { repository, queue: new Map<string, PromiseSource<FileDecoration | undefined>>() };
|
||||
this.queue.set(repository.root, queueItem);
|
||||
}
|
||||
|
||||
let promiseSource = queueItem.queue.get(uri.fsPath);
|
||||
|
||||
if (!promiseSource) {
|
||||
promiseSource = new PromiseSource();
|
||||
queueItem!.queue.set(uri.fsPath, promiseSource);
|
||||
this.checkIgnoreSoon();
|
||||
}
|
||||
|
||||
return await promiseSource.promise;
|
||||
}
|
||||
|
||||
@debounce(500)
|
||||
private checkIgnoreSoon(): void {
|
||||
const queue = new Map(this.queue.entries());
|
||||
this.queue.clear();
|
||||
|
||||
for (const [, item] of queue) {
|
||||
const paths = [...item.queue.keys()];
|
||||
|
||||
item.repository.checkIgnore(paths).then(ignoreSet => {
|
||||
for (const [path, promiseSource] of item.queue.entries()) {
|
||||
promiseSource.resolve(ignoreSet.has(path) ? GitIgnoreDecorationProvider.Decoration : undefined);
|
||||
}
|
||||
}, err => {
|
||||
if (err.gitErrorCode !== GitErrorCodes.IsInSubmodule) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
for (const [, promiseSource] of item.queue.entries()) {
|
||||
promiseSource.reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
this.queue.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class GitDecorationProvider implements FileDecorationProvider {
|
||||
|
||||
private static SubmoduleDecorationData: FileDecoration = {
|
||||
tooltip: 'Submodule',
|
||||
badge: 'S',
|
||||
color: new ThemeColor('gitDecoration.submoduleResourceForeground')
|
||||
};
|
||||
|
||||
private readonly _onDidChangeDecorations = new EventEmitter<Uri[]>();
|
||||
readonly onDidChange: Event<Uri[]> = this._onDidChangeDecorations.event;
|
||||
|
||||
private disposables: Disposable[] = [];
|
||||
private decorations = new Map<string, FileDecoration>();
|
||||
|
||||
constructor(private repository: Repository) {
|
||||
this.disposables.push(
|
||||
window.registerDecorationProvider(this),
|
||||
repository.onDidRunGitStatus(this.onDidRunGitStatus, this)
|
||||
);
|
||||
}
|
||||
|
||||
private onDidRunGitStatus(): void {
|
||||
let newDecorations = new Map<string, FileDecoration>();
|
||||
|
||||
this.collectSubmoduleDecorationData(newDecorations);
|
||||
this.collectDecorationData(this.repository.indexGroup, newDecorations);
|
||||
this.collectDecorationData(this.repository.untrackedGroup, newDecorations);
|
||||
this.collectDecorationData(this.repository.workingTreeGroup, newDecorations);
|
||||
this.collectDecorationData(this.repository.mergeGroup, newDecorations);
|
||||
|
||||
const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()]));
|
||||
this.decorations = newDecorations;
|
||||
this._onDidChangeDecorations.fire([...uris.values()].map(value => Uri.parse(value, true)));
|
||||
}
|
||||
|
||||
private collectDecorationData(group: GitResourceGroup, bucket: Map<string, FileDecoration>): void {
|
||||
for (const r of group.resourceStates) {
|
||||
const decoration = r.resourceDecoration;
|
||||
|
||||
if (decoration) {
|
||||
// not deleted and has a decoration
|
||||
bucket.set(r.original.toString(), decoration);
|
||||
|
||||
if (r.type === Status.INDEX_RENAMED) {
|
||||
bucket.set(r.resourceUri.toString(), decoration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private collectSubmoduleDecorationData(bucket: Map<string, FileDecoration>): void {
|
||||
for (const submodule of this.repository.submodules) {
|
||||
bucket.set(Uri.file(path.join(this.repository.root, submodule.path)).toString(), GitDecorationProvider.SubmoduleDecorationData);
|
||||
}
|
||||
}
|
||||
|
||||
provideFileDecoration(uri: Uri): FileDecoration | undefined {
|
||||
return this.decorations.get(uri.toString());
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class GitDecorations {
|
||||
|
||||
private disposables: Disposable[] = [];
|
||||
private modelDisposables: Disposable[] = [];
|
||||
private providers = new Map<Repository, Disposable>();
|
||||
|
||||
constructor(private model: Model) {
|
||||
this.disposables.push(new GitIgnoreDecorationProvider(model));
|
||||
|
||||
const onEnablementChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.decorations.enabled'));
|
||||
onEnablementChange(this.update, this, this.disposables);
|
||||
this.update();
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
const enabled = workspace.getConfiguration('git').get('decorations.enabled');
|
||||
|
||||
if (enabled) {
|
||||
this.enable();
|
||||
} else {
|
||||
this.disable();
|
||||
}
|
||||
}
|
||||
|
||||
private enable(): void {
|
||||
this.model.onDidOpenRepository(this.onDidOpenRepository, this, this.modelDisposables);
|
||||
this.model.onDidCloseRepository(this.onDidCloseRepository, this, this.modelDisposables);
|
||||
this.model.repositories.forEach(this.onDidOpenRepository, this);
|
||||
}
|
||||
|
||||
private disable(): void {
|
||||
this.modelDisposables = dispose(this.modelDisposables);
|
||||
this.providers.forEach(value => value.dispose());
|
||||
this.providers.clear();
|
||||
}
|
||||
|
||||
private onDidOpenRepository(repository: Repository): void {
|
||||
const provider = new GitDecorationProvider(repository);
|
||||
this.providers.set(repository, provider);
|
||||
}
|
||||
|
||||
private onDidCloseRepository(repository: Repository): void {
|
||||
const provider = this.providers.get(repository);
|
||||
|
||||
if (provider) {
|
||||
provider.dispose();
|
||||
this.providers.delete(repository);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disable();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
101
lib/vscode/extensions/git/src/decorators.ts
Normal file
@ -0,0 +1,101 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { done } from './util';
|
||||
|
||||
function decorate(decorator: (fn: Function, key: string) => Function): Function {
|
||||
return (_target: any, key: string, descriptor: any) => {
|
||||
let fnKey: string | null = null;
|
||||
let fn: Function | null = null;
|
||||
|
||||
if (typeof descriptor.value === 'function') {
|
||||
fnKey = 'value';
|
||||
fn = descriptor.value;
|
||||
} else if (typeof descriptor.get === 'function') {
|
||||
fnKey = 'get';
|
||||
fn = descriptor.get;
|
||||
}
|
||||
|
||||
if (!fn || !fnKey) {
|
||||
throw new Error('not supported');
|
||||
}
|
||||
|
||||
descriptor[fnKey] = decorator(fn, key);
|
||||
};
|
||||
}
|
||||
|
||||
function _memoize(fn: Function, key: string): Function {
|
||||
const memoizeKey = `$memoize$${key}`;
|
||||
|
||||
return function (this: any, ...args: any[]) {
|
||||
if (!this.hasOwnProperty(memoizeKey)) {
|
||||
Object.defineProperty(this, memoizeKey, {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: fn.apply(this, args)
|
||||
});
|
||||
}
|
||||
|
||||
return this[memoizeKey];
|
||||
};
|
||||
}
|
||||
|
||||
export const memoize = decorate(_memoize);
|
||||
|
||||
function _throttle<T>(fn: Function, key: string): Function {
|
||||
const currentKey = `$throttle$current$${key}`;
|
||||
const nextKey = `$throttle$next$${key}`;
|
||||
|
||||
const trigger = function (this: any, ...args: any[]) {
|
||||
if (this[nextKey]) {
|
||||
return this[nextKey];
|
||||
}
|
||||
|
||||
if (this[currentKey]) {
|
||||
this[nextKey] = done(this[currentKey]).then(() => {
|
||||
this[nextKey] = undefined;
|
||||
return trigger.apply(this, args);
|
||||
});
|
||||
|
||||
return this[nextKey];
|
||||
}
|
||||
|
||||
this[currentKey] = fn.apply(this, args) as Promise<T>;
|
||||
|
||||
const clear = () => this[currentKey] = undefined;
|
||||
done(this[currentKey]).then(clear, clear);
|
||||
|
||||
return this[currentKey];
|
||||
};
|
||||
|
||||
return trigger;
|
||||
}
|
||||
|
||||
export const throttle = decorate(_throttle);
|
||||
|
||||
function _sequentialize(fn: Function, key: string): Function {
|
||||
const currentKey = `__$sequence$${key}`;
|
||||
|
||||
return function (this: any, ...args: any[]) {
|
||||
const currentPromise = this[currentKey] as Promise<any> || Promise.resolve(null);
|
||||
const run = async () => await fn.apply(this, args);
|
||||
this[currentKey] = currentPromise.then(run, run);
|
||||
return this[currentKey];
|
||||
};
|
||||
}
|
||||
|
||||
export const sequentialize = decorate(_sequentialize);
|
||||
|
||||
export function debounce(delay: number): Function {
|
||||
return decorate((fn, key) => {
|
||||
const timerKey = `$debounce$${key}`;
|
||||
|
||||
return function (this: any, ...args: any[]) {
|
||||
clearTimeout(this[timerKey]);
|
||||
this[timerKey] = setTimeout(() => fn.apply(this, args), delay);
|
||||
};
|
||||
});
|
||||
}
|
39
lib/vscode/extensions/git/src/emoji.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import { workspace, Uri } from 'vscode';
|
||||
import { getExtensionContext } from './main';
|
||||
import { TextDecoder } from 'util';
|
||||
|
||||
const emojiRegex = /:([-+_a-z0-9]+):/g;
|
||||
|
||||
let emojiMap: Record<string, string> | undefined;
|
||||
let emojiMapPromise: Promise<void> | undefined;
|
||||
|
||||
export async function ensureEmojis() {
|
||||
if (emojiMap === undefined) {
|
||||
if (emojiMapPromise === undefined) {
|
||||
emojiMapPromise = loadEmojiMap();
|
||||
}
|
||||
await emojiMapPromise;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadEmojiMap() {
|
||||
const context = getExtensionContext();
|
||||
const uri = (Uri as any).joinPath(context.extensionUri, 'resources', 'emojis.json');
|
||||
emojiMap = JSON.parse(new TextDecoder('utf8').decode(await workspace.fs.readFile(uri)));
|
||||
}
|
||||
|
||||
export function emojify(message: string) {
|
||||
if (emojiMap === undefined) {
|
||||
return message;
|
||||
}
|
||||
|
||||
return message.replace(emojiRegex, (s, code) => {
|
||||
return emojiMap?.[code] || s;
|
||||
});
|
||||
}
|
77
lib/vscode/extensions/git/src/encoding.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as jschardet from 'jschardet';
|
||||
|
||||
function detectEncodingByBOM(buffer: Buffer): string | null {
|
||||
if (!buffer || buffer.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const b0 = buffer.readUInt8(0);
|
||||
const b1 = buffer.readUInt8(1);
|
||||
|
||||
// UTF-16 BE
|
||||
if (b0 === 0xFE && b1 === 0xFF) {
|
||||
return 'utf16be';
|
||||
}
|
||||
|
||||
// UTF-16 LE
|
||||
if (b0 === 0xFF && b1 === 0xFE) {
|
||||
return 'utf16le';
|
||||
}
|
||||
|
||||
if (buffer.length < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const b2 = buffer.readUInt8(2);
|
||||
|
||||
// UTF-8
|
||||
if (b0 === 0xEF && b1 === 0xBB && b2 === 0xBF) {
|
||||
return 'utf8';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const IGNORE_ENCODINGS = [
|
||||
'ascii',
|
||||
'utf-8',
|
||||
'utf-16',
|
||||
'utf-32'
|
||||
];
|
||||
|
||||
const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = {
|
||||
'ibm866': 'cp866',
|
||||
'big5': 'cp950'
|
||||
};
|
||||
|
||||
export function detectEncoding(buffer: Buffer): string | null {
|
||||
let result = detectEncodingByBOM(buffer);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const detected = jschardet.detect(buffer);
|
||||
|
||||
if (!detected || !detected.encoding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const encoding = detected.encoding;
|
||||
|
||||
// Ignore encodings that cannot guess correctly
|
||||
// (http://chardet.readthedocs.io/en/latest/supported-encodings.html)
|
||||
if (0 <= IGNORE_ENCODINGS.indexOf(encoding.toLowerCase())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizedEncodingName = encoding.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
|
||||
const mapped = JSCHARDET_TO_ICONV_ENCODINGS[normalizedEncodingName];
|
||||
|
||||
return mapped || normalizedEncodingName;
|
||||
}
|
216
lib/vscode/extensions/git/src/fileSystemProvider.ts
Normal file
@ -0,0 +1,216 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { workspace, Uri, Disposable, Event, EventEmitter, window, FileSystemProvider, FileChangeEvent, FileStat, FileType, FileChangeType, FileSystemError } from 'vscode';
|
||||
import { debounce, throttle } from './decorators';
|
||||
import { fromGitUri, toGitUri } from './uri';
|
||||
import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model';
|
||||
import { filterEvent, eventToPromise, isDescendant, pathEquals, EmptyDisposable } from './util';
|
||||
import { Repository } from './repository';
|
||||
|
||||
interface CacheRow {
|
||||
uri: Uri;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
const THREE_MINUTES = 1000 * 60 * 3;
|
||||
const FIVE_MINUTES = 1000 * 60 * 5;
|
||||
|
||||
function sanitizeRef(ref: string, path: string, repository: Repository): string {
|
||||
if (ref === '~') {
|
||||
const fileUri = Uri.file(path);
|
||||
const uriString = fileUri.toString();
|
||||
const [indexStatus] = repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString);
|
||||
return indexStatus ? '' : 'HEAD';
|
||||
}
|
||||
|
||||
if (/^~\d$/.test(ref)) {
|
||||
return `:${ref[1]}`;
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
export class GitFileSystemProvider implements FileSystemProvider {
|
||||
|
||||
private _onDidChangeFile = new EventEmitter<FileChangeEvent[]>();
|
||||
readonly onDidChangeFile: Event<FileChangeEvent[]> = this._onDidChangeFile.event;
|
||||
|
||||
private changedRepositoryRoots = new Set<string>();
|
||||
private cache = new Map<string, CacheRow>();
|
||||
private mtime = new Date().getTime();
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(private model: Model) {
|
||||
this.disposables.push(
|
||||
model.onDidChangeRepository(this.onDidChangeRepository, this),
|
||||
model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this),
|
||||
workspace.registerFileSystemProvider('git', this, { isReadonly: true, isCaseSensitive: true }),
|
||||
workspace.registerResourceLabelFormatter({
|
||||
scheme: 'git',
|
||||
formatting: {
|
||||
label: '${path} (git)',
|
||||
separator: '/'
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setInterval(() => this.cleanup(), FIVE_MINUTES);
|
||||
}
|
||||
|
||||
private onDidChangeRepository({ repository }: ModelChangeEvent): void {
|
||||
this.changedRepositoryRoots.add(repository.root);
|
||||
this.eventuallyFireChangeEvents();
|
||||
}
|
||||
|
||||
private onDidChangeOriginalResource({ uri }: OriginalResourceChangeEvent): void {
|
||||
if (uri.scheme !== 'file') {
|
||||
return;
|
||||
}
|
||||
|
||||
const gitUri = toGitUri(uri, '', { replaceFileExtension: true });
|
||||
this.mtime = new Date().getTime();
|
||||
this._onDidChangeFile.fire([{ type: FileChangeType.Changed, uri: gitUri }]);
|
||||
}
|
||||
|
||||
@debounce(1100)
|
||||
private eventuallyFireChangeEvents(): void {
|
||||
this.fireChangeEvents();
|
||||
}
|
||||
|
||||
@throttle
|
||||
private async fireChangeEvents(): Promise<void> {
|
||||
if (!window.state.focused) {
|
||||
const onDidFocusWindow = filterEvent(window.onDidChangeWindowState, e => e.focused);
|
||||
await eventToPromise(onDidFocusWindow);
|
||||
}
|
||||
|
||||
const events: FileChangeEvent[] = [];
|
||||
|
||||
for (const { uri } of this.cache.values()) {
|
||||
const fsPath = uri.fsPath;
|
||||
|
||||
for (const root of this.changedRepositoryRoots) {
|
||||
if (isDescendant(root, fsPath)) {
|
||||
events.push({ type: FileChangeType.Changed, uri });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (events.length > 0) {
|
||||
this.mtime = new Date().getTime();
|
||||
this._onDidChangeFile.fire(events);
|
||||
}
|
||||
|
||||
this.changedRepositoryRoots.clear();
|
||||
}
|
||||
|
||||
private cleanup(): void {
|
||||
const now = new Date().getTime();
|
||||
const cache = new Map<string, CacheRow>();
|
||||
|
||||
for (const row of this.cache.values()) {
|
||||
const { path } = fromGitUri(row.uri);
|
||||
const isOpen = workspace.textDocuments
|
||||
.filter(d => d.uri.scheme === 'file')
|
||||
.some(d => pathEquals(d.uri.fsPath, path));
|
||||
|
||||
if (isOpen || now - row.timestamp < THREE_MINUTES) {
|
||||
cache.set(row.uri.toString(), row);
|
||||
} else {
|
||||
// TODO: should fire delete events?
|
||||
}
|
||||
}
|
||||
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
watch(): Disposable {
|
||||
return EmptyDisposable;
|
||||
}
|
||||
|
||||
async stat(uri: Uri): Promise<FileStat> {
|
||||
await this.model.isInitialized;
|
||||
|
||||
const { submoduleOf, path, ref } = fromGitUri(uri);
|
||||
const repository = submoduleOf ? this.model.getRepository(submoduleOf) : this.model.getRepository(uri);
|
||||
if (!repository) {
|
||||
throw FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
let size = 0;
|
||||
try {
|
||||
const details = await repository.getObjectDetails(sanitizeRef(ref, path, repository), path);
|
||||
size = details.size;
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
return { type: FileType.File, size: size, mtime: this.mtime, ctime: 0 };
|
||||
}
|
||||
|
||||
readDirectory(): Thenable<[string, FileType][]> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
createDirectory(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async readFile(uri: Uri): Promise<Uint8Array> {
|
||||
await this.model.isInitialized;
|
||||
|
||||
const { path, ref, submoduleOf } = fromGitUri(uri);
|
||||
|
||||
if (submoduleOf) {
|
||||
const repository = this.model.getRepository(submoduleOf);
|
||||
|
||||
if (!repository) {
|
||||
throw FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
if (ref === 'index') {
|
||||
return encoder.encode(await repository.diffIndexWithHEAD(path));
|
||||
} else {
|
||||
return encoder.encode(await repository.diffWithHEAD(path));
|
||||
}
|
||||
}
|
||||
|
||||
const repository = this.model.getRepository(uri);
|
||||
|
||||
if (!repository) {
|
||||
throw FileSystemError.FileNotFound();
|
||||
}
|
||||
|
||||
const timestamp = new Date().getTime();
|
||||
const cacheValue: CacheRow = { uri, timestamp };
|
||||
|
||||
this.cache.set(uri.toString(), cacheValue);
|
||||
|
||||
try {
|
||||
return await repository.buffer(sanitizeRef(ref, path, repository), path);
|
||||
} catch (err) {
|
||||
return new Uint8Array(0);
|
||||
}
|
||||
}
|
||||
|
||||
writeFile(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
delete(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
rename(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
}
|
||||
}
|
2062
lib/vscode/extensions/git/src/git.ts
Normal file
45
lib/vscode/extensions/git/src/ipc/ipcClient.ts
Normal file
@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as http from 'http';
|
||||
|
||||
export class IPCClient {
|
||||
|
||||
private ipcHandlePath: string;
|
||||
|
||||
constructor(private handlerName: string) {
|
||||
const ipcHandlePath = process.env['VSCODE_GIT_IPC_HANDLE'];
|
||||
|
||||
if (!ipcHandlePath) {
|
||||
throw new Error('Missing VSCODE_GIT_IPC_HANDLE');
|
||||
}
|
||||
|
||||
this.ipcHandlePath = ipcHandlePath;
|
||||
}
|
||||
|
||||
call(request: any): Promise<any> {
|
||||
const opts: http.RequestOptions = {
|
||||
socketPath: this.ipcHandlePath,
|
||||
path: `/${this.handlerName}`,
|
||||
method: 'POST'
|
||||
};
|
||||
|
||||
return new Promise((c, e) => {
|
||||
const req = http.request(opts, res => {
|
||||
if (res.statusCode !== 200) {
|
||||
return e(new Error(`Bad status code: ${res.statusCode}`));
|
||||
}
|
||||
|
||||
const chunks: Buffer[] = [];
|
||||
res.on('data', d => chunks.push(d));
|
||||
res.on('end', () => c(JSON.parse(Buffer.concat(chunks).toString('utf8'))));
|
||||
});
|
||||
|
||||
req.on('error', err => e(err));
|
||||
req.write(JSON.stringify(request));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
}
|
121
lib/vscode/extensions/git/src/ipc/ipcServer.ts
Normal file
@ -0,0 +1,121 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vscode';
|
||||
import { toDisposable } from '../util';
|
||||
import * as path from 'path';
|
||||
import * as http from 'http';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
function getIPCHandlePath(id: string): string {
|
||||
if (process.platform === 'win32') {
|
||||
return `\\\\.\\pipe\\vscode-git-${id}-sock`;
|
||||
}
|
||||
|
||||
if (process.env['XDG_RUNTIME_DIR']) {
|
||||
return path.join(process.env['XDG_RUNTIME_DIR'] as string, `vscode-git-${id}.sock`);
|
||||
}
|
||||
|
||||
return path.join(os.tmpdir(), `vscode-git-${id}.sock`);
|
||||
}
|
||||
|
||||
export interface IIPCHandler {
|
||||
handle(request: any): Promise<any>;
|
||||
}
|
||||
|
||||
export async function createIPCServer(context?: string): Promise<IIPCServer> {
|
||||
const server = http.createServer();
|
||||
const hash = crypto.createHash('sha1');
|
||||
|
||||
if (!context) {
|
||||
const buffer = await new Promise<Buffer>((c, e) => crypto.randomBytes(20, (err, buf) => err ? e(err) : c(buf)));
|
||||
hash.update(buffer);
|
||||
} else {
|
||||
hash.update(context);
|
||||
}
|
||||
|
||||
const ipcHandlePath = getIPCHandlePath(hash.digest('hex').substr(0, 10));
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
try {
|
||||
await fs.promises.unlink(ipcHandlePath);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((c, e) => {
|
||||
try {
|
||||
server.on('error', err => e(err));
|
||||
server.listen(ipcHandlePath);
|
||||
c(new IPCServer(server, ipcHandlePath));
|
||||
} catch (err) {
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export interface IIPCServer extends Disposable {
|
||||
readonly ipcHandlePath: string | undefined;
|
||||
getEnv(): { [key: string]: string; };
|
||||
registerHandler(name: string, handler: IIPCHandler): Disposable;
|
||||
}
|
||||
|
||||
class IPCServer implements IIPCServer, Disposable {
|
||||
|
||||
private handlers = new Map<string, IIPCHandler>();
|
||||
get ipcHandlePath(): string { return this._ipcHandlePath; }
|
||||
|
||||
constructor(private server: http.Server, private _ipcHandlePath: string) {
|
||||
this.server.on('request', this.onRequest.bind(this));
|
||||
}
|
||||
|
||||
registerHandler(name: string, handler: IIPCHandler): Disposable {
|
||||
this.handlers.set(`/${name}`, handler);
|
||||
return toDisposable(() => this.handlers.delete(name));
|
||||
}
|
||||
|
||||
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
|
||||
if (!req.url) {
|
||||
console.warn(`Request lacks url`);
|
||||
return;
|
||||
}
|
||||
|
||||
const handler = this.handlers.get(req.url);
|
||||
|
||||
if (!handler) {
|
||||
console.warn(`IPC handler for ${req.url} not found`);
|
||||
return;
|
||||
}
|
||||
|
||||
const chunks: Buffer[] = [];
|
||||
req.on('data', d => chunks.push(d));
|
||||
req.on('end', () => {
|
||||
const request = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
||||
handler.handle(request).then(result => {
|
||||
res.writeHead(200);
|
||||
res.end(JSON.stringify(result));
|
||||
}, () => {
|
||||
res.writeHead(500);
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getEnv(): { [key: string]: string; } {
|
||||
return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath };
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.handlers.clear();
|
||||
this.server.close();
|
||||
|
||||
if (this._ipcHandlePath && process.platform !== 'win32') {
|
||||
fs.unlinkSync(this._ipcHandlePath);
|
||||
}
|
||||
}
|
||||
}
|
50
lib/vscode/extensions/git/src/log.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, EventEmitter } from 'vscode';
|
||||
|
||||
/**
|
||||
* The severity level of a log message
|
||||
*/
|
||||
export enum LogLevel {
|
||||
Trace = 1,
|
||||
Debug = 2,
|
||||
Info = 3,
|
||||
Warning = 4,
|
||||
Error = 5,
|
||||
Critical = 6,
|
||||
Off = 7
|
||||
}
|
||||
|
||||
let _logLevel: LogLevel = LogLevel.Info;
|
||||
const _onDidChangeLogLevel = new EventEmitter<LogLevel>();
|
||||
|
||||
export const Log = {
|
||||
/**
|
||||
* Current logging level.
|
||||
*/
|
||||
get logLevel(): LogLevel {
|
||||
return _logLevel;
|
||||
},
|
||||
|
||||
/**
|
||||
* Current logging level.
|
||||
*/
|
||||
set logLevel(logLevel: LogLevel) {
|
||||
if (_logLevel === logLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
_logLevel = logLevel;
|
||||
_onDidChangeLogLevel.fire(logLevel);
|
||||
},
|
||||
|
||||
/**
|
||||
* An [event](#Event) that fires when the log level has changed.
|
||||
*/
|
||||
get onDidChangeLogLevel(): Event<LogLevel> {
|
||||
return _onDidChangeLogLevel.event;
|
||||
}
|
||||
};
|
246
lib/vscode/extensions/git/src/main.ts
Normal file
@ -0,0 +1,246 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { ExtensionContext, workspace, window, Disposable, commands, Uri, OutputChannel, WorkspaceFolder } from 'vscode';
|
||||
import { findGit, Git, IGit } from './git';
|
||||
import { Model } from './model';
|
||||
import { CommandCenter } from './commands';
|
||||
import { GitFileSystemProvider } from './fileSystemProvider';
|
||||
import { GitDecorations } from './decorationProvider';
|
||||
import { Askpass } from './askpass';
|
||||
import { toDisposable, filterEvent, eventToPromise } from './util';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import { GitExtension } from './api/git';
|
||||
import { GitProtocolHandler } from './protocolHandler';
|
||||
import { GitExtensionImpl } from './api/extension';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { GitTimelineProvider } from './timelineProvider';
|
||||
import { registerAPICommands } from './api/api1';
|
||||
import { TerminalEnvironmentManager } from './terminal';
|
||||
|
||||
const deactivateTasks: { (): Promise<any>; }[] = [];
|
||||
|
||||
export async function deactivate(): Promise<any> {
|
||||
for (const task of deactivateTasks) {
|
||||
await task();
|
||||
}
|
||||
}
|
||||
|
||||
async function createModel(context: ExtensionContext, outputChannel: OutputChannel, telemetryReporter: TelemetryReporter, disposables: Disposable[]): Promise<Model> {
|
||||
const pathHint = workspace.getConfiguration('git').get<string | string[]>('path');
|
||||
const info = await findGit(pathHint, path => outputChannel.appendLine(localize('looking', "Looking for git in: {0}", path)));
|
||||
|
||||
const askpass = await Askpass.create(outputChannel, context.storagePath);
|
||||
disposables.push(askpass);
|
||||
|
||||
const env = askpass.getEnv();
|
||||
const terminalEnvironmentManager = new TerminalEnvironmentManager(context, env);
|
||||
disposables.push(terminalEnvironmentManager);
|
||||
|
||||
const git = new Git({ gitPath: info.path, version: info.version, env });
|
||||
const model = new Model(git, askpass, context.globalState, outputChannel);
|
||||
disposables.push(model);
|
||||
|
||||
const onRepository = () => commands.executeCommand('setContext', 'gitOpenRepositoryCount', `${model.repositories.length}`);
|
||||
model.onDidOpenRepository(onRepository, null, disposables);
|
||||
model.onDidCloseRepository(onRepository, null, disposables);
|
||||
onRepository();
|
||||
|
||||
outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path));
|
||||
|
||||
const onOutput = (str: string) => {
|
||||
const lines = str.split(/\r?\n/mg);
|
||||
|
||||
while (/^\s*$/.test(lines[lines.length - 1])) {
|
||||
lines.pop();
|
||||
}
|
||||
|
||||
outputChannel.appendLine(lines.join('\n'));
|
||||
};
|
||||
git.onOutput.addListener('log', onOutput);
|
||||
disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput)));
|
||||
|
||||
disposables.push(
|
||||
new CommandCenter(git, model, outputChannel, telemetryReporter),
|
||||
new GitFileSystemProvider(model),
|
||||
new GitDecorations(model),
|
||||
new GitProtocolHandler(),
|
||||
new GitTimelineProvider(model)
|
||||
);
|
||||
|
||||
checkGitVersion(info);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
async function isGitRepository(folder: WorkspaceFolder): Promise<boolean> {
|
||||
if (folder.uri.scheme !== 'file') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dotGit = path.join(folder.uri.fsPath, '.git');
|
||||
|
||||
try {
|
||||
const dotGitStat = await new Promise<fs.Stats>((c, e) => fs.stat(dotGit, (err, stat) => err ? e(err) : c(stat)));
|
||||
return dotGitStat.isDirectory();
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function warnAboutMissingGit(): Promise<void> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const shouldIgnore = config.get<boolean>('ignoreMissingGitWarning') === true;
|
||||
|
||||
if (shouldIgnore) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!workspace.workspaceFolders) {
|
||||
return;
|
||||
}
|
||||
|
||||
const areGitRepositories = await Promise.all(workspace.workspaceFolders.map(isGitRepository));
|
||||
|
||||
if (areGitRepositories.every(isGitRepository => !isGitRepository)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const download = localize('downloadgit', "Download Git");
|
||||
const neverShowAgain = localize('neverShowAgain', "Don't Show Again");
|
||||
const choice = await window.showWarningMessage(
|
||||
localize('notfound', "Git not found. Install it or configure it using the 'git.path' setting."),
|
||||
download,
|
||||
neverShowAgain
|
||||
);
|
||||
|
||||
if (choice === download) {
|
||||
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
|
||||
} else if (choice === neverShowAgain) {
|
||||
await config.update('ignoreMissingGitWarning', true, true);
|
||||
}
|
||||
}
|
||||
|
||||
export async function _activate(context: ExtensionContext): Promise<GitExtensionImpl> {
|
||||
const disposables: Disposable[] = [];
|
||||
context.subscriptions.push(new Disposable(() => Disposable.from(...disposables).dispose()));
|
||||
|
||||
const outputChannel = window.createOutputChannel('Git');
|
||||
commands.registerCommand('git.showOutput', () => outputChannel.show());
|
||||
disposables.push(outputChannel);
|
||||
|
||||
const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string };
|
||||
const telemetryReporter = new TelemetryReporter(name, version, aiKey);
|
||||
deactivateTasks.push(() => telemetryReporter.dispose());
|
||||
|
||||
const config = workspace.getConfiguration('git', null);
|
||||
const enabled = config.get<boolean>('enabled');
|
||||
|
||||
if (!enabled) {
|
||||
const onConfigChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git'));
|
||||
const onEnabled = filterEvent(onConfigChange, () => workspace.getConfiguration('git', null).get<boolean>('enabled') === true);
|
||||
const result = new GitExtensionImpl();
|
||||
|
||||
eventToPromise(onEnabled).then(async () => result.model = await createModel(context, outputChannel, telemetryReporter, disposables));
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
const model = await createModel(context, outputChannel, telemetryReporter, disposables);
|
||||
return new GitExtensionImpl(model);
|
||||
} catch (err) {
|
||||
if (!/Git installation not found/.test(err.message || '')) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
console.warn(err.message);
|
||||
outputChannel.appendLine(err.message);
|
||||
|
||||
commands.executeCommand('setContext', 'git.missing', true);
|
||||
warnAboutMissingGit();
|
||||
|
||||
return new GitExtensionImpl();
|
||||
}
|
||||
}
|
||||
|
||||
let _context: ExtensionContext;
|
||||
export function getExtensionContext(): ExtensionContext {
|
||||
return _context;
|
||||
}
|
||||
|
||||
export async function activate(context: ExtensionContext): Promise<GitExtension> {
|
||||
_context = context;
|
||||
|
||||
const result = await _activate(context);
|
||||
context.subscriptions.push(registerAPICommands(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
async function checkGitv1(info: IGit): Promise<void> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const shouldIgnore = config.get<boolean>('ignoreLegacyWarning') === true;
|
||||
|
||||
if (shouldIgnore) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[01]/.test(info.version)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const update = localize('updateGit', "Update Git");
|
||||
const neverShowAgain = localize('neverShowAgain', "Don't Show Again");
|
||||
|
||||
const choice = await window.showWarningMessage(
|
||||
localize('git20', "You seem to have git {0} installed. Code works best with git >= 2", info.version),
|
||||
update,
|
||||
neverShowAgain
|
||||
);
|
||||
|
||||
if (choice === update) {
|
||||
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
|
||||
} else if (choice === neverShowAgain) {
|
||||
await config.update('ignoreLegacyWarning', true, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function checkGitWindows(info: IGit): Promise<void> {
|
||||
if (!/^2\.(25|26)\./.test(info.version)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = workspace.getConfiguration('git');
|
||||
const shouldIgnore = config.get<boolean>('ignoreWindowsGit27Warning') === true;
|
||||
|
||||
if (shouldIgnore) {
|
||||
return;
|
||||
}
|
||||
|
||||
const update = localize('updateGit', "Update Git");
|
||||
const neverShowAgain = localize('neverShowAgain', "Don't Show Again");
|
||||
const choice = await window.showWarningMessage(
|
||||
localize('git2526', "There are known issues with the installed Git {0}. Please update to Git >= 2.27 for the git features to work correctly.", info.version),
|
||||
update,
|
||||
neverShowAgain
|
||||
);
|
||||
|
||||
if (choice === update) {
|
||||
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
|
||||
} else if (choice === neverShowAgain) {
|
||||
await config.update('ignoreWindowsGit27Warning', true, true);
|
||||
}
|
||||
}
|
||||
|
||||
async function checkGitVersion(info: IGit): Promise<void> {
|
||||
await checkGitv1(info);
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
await checkGitWindows(info);
|
||||
}
|
||||
}
|
508
lib/vscode/extensions/git/src/model.ts
Normal file
@ -0,0 +1,508 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, OutputChannel, commands } from 'vscode';
|
||||
import { Repository, RepositoryState } from './repository';
|
||||
import { memoize, sequentialize, debounce } from './decorators';
|
||||
import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util';
|
||||
import { Git } from './git';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { fromGitUri } from './uri';
|
||||
import { APIState as State, RemoteSourceProvider, CredentialsProvider, PushErrorHandler } from './api/git';
|
||||
import { Askpass } from './askpass';
|
||||
import { IRemoteSourceProviderRegistry } from './remoteProvider';
|
||||
import { IPushErrorHandlerRegistry } from './pushError';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
class RepositoryPick implements QuickPickItem {
|
||||
@memoize get label(): string {
|
||||
return path.basename(this.repository.root);
|
||||
}
|
||||
|
||||
@memoize get description(): string {
|
||||
return [this.repository.headLabel, this.repository.syncLabel]
|
||||
.filter(l => !!l)
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
constructor(public readonly repository: Repository, public readonly index: number) { }
|
||||
}
|
||||
|
||||
export interface ModelChangeEvent {
|
||||
repository: Repository;
|
||||
uri: Uri;
|
||||
}
|
||||
|
||||
export interface OriginalResourceChangeEvent {
|
||||
repository: Repository;
|
||||
uri: Uri;
|
||||
}
|
||||
|
||||
interface OpenRepository extends Disposable {
|
||||
repository: Repository;
|
||||
}
|
||||
|
||||
export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRegistry {
|
||||
|
||||
private _onDidOpenRepository = new EventEmitter<Repository>();
|
||||
readonly onDidOpenRepository: Event<Repository> = this._onDidOpenRepository.event;
|
||||
|
||||
private _onDidCloseRepository = new EventEmitter<Repository>();
|
||||
readonly onDidCloseRepository: Event<Repository> = this._onDidCloseRepository.event;
|
||||
|
||||
private _onDidChangeRepository = new EventEmitter<ModelChangeEvent>();
|
||||
readonly onDidChangeRepository: Event<ModelChangeEvent> = this._onDidChangeRepository.event;
|
||||
|
||||
private _onDidChangeOriginalResource = new EventEmitter<OriginalResourceChangeEvent>();
|
||||
readonly onDidChangeOriginalResource: Event<OriginalResourceChangeEvent> = this._onDidChangeOriginalResource.event;
|
||||
|
||||
private openRepositories: OpenRepository[] = [];
|
||||
get repositories(): Repository[] { return this.openRepositories.map(r => r.repository); }
|
||||
|
||||
private possibleGitRepositoryPaths = new Set<string>();
|
||||
|
||||
private _onDidChangeState = new EventEmitter<State>();
|
||||
readonly onDidChangeState = this._onDidChangeState.event;
|
||||
|
||||
private _state: State = 'uninitialized';
|
||||
get state(): State { return this._state; }
|
||||
|
||||
setState(state: State): void {
|
||||
this._state = state;
|
||||
this._onDidChangeState.fire(state);
|
||||
commands.executeCommand('setContext', 'git.state', state);
|
||||
}
|
||||
|
||||
@memoize
|
||||
get isInitialized(): Promise<void> {
|
||||
if (this._state === 'initialized') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise<any>;
|
||||
}
|
||||
|
||||
private remoteSourceProviders = new Set<RemoteSourceProvider>();
|
||||
|
||||
private _onDidAddRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
|
||||
readonly onDidAddRemoteSourceProvider = this._onDidAddRemoteSourceProvider.event;
|
||||
|
||||
private _onDidRemoveRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
|
||||
readonly onDidRemoveRemoteSourceProvider = this._onDidRemoveRemoteSourceProvider.event;
|
||||
|
||||
private pushErrorHandlers = new Set<PushErrorHandler>();
|
||||
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannel: OutputChannel) {
|
||||
workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);
|
||||
window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);
|
||||
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);
|
||||
|
||||
const fsWatcher = workspace.createFileSystemWatcher('**');
|
||||
this.disposables.push(fsWatcher);
|
||||
|
||||
const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);
|
||||
const onGitRepositoryChange = filterEvent(onWorkspaceChange, uri => /\/\.git/.test(uri.path));
|
||||
const onPossibleGitRepositoryChange = filterEvent(onGitRepositoryChange, uri => !this.getRepository(uri));
|
||||
onPossibleGitRepositoryChange(this.onPossibleGitRepositoryChange, this, this.disposables);
|
||||
|
||||
this.setState('uninitialized');
|
||||
this.doInitialScan().finally(() => this.setState('initialized'));
|
||||
}
|
||||
|
||||
private async doInitialScan(): Promise<void> {
|
||||
await Promise.all([
|
||||
this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),
|
||||
this.onDidChangeVisibleTextEditors(window.visibleTextEditors),
|
||||
this.scanWorkspaceFolders()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the first level of each workspace folder, looking
|
||||
* for git repositories.
|
||||
*/
|
||||
private async scanWorkspaceFolders(): Promise<void> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
|
||||
|
||||
if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all((workspace.workspaceFolders || []).map(async folder => {
|
||||
const root = folder.uri.fsPath;
|
||||
const children = await new Promise<string[]>((c, e) => fs.readdir(root, (err, r) => err ? e(err) : c(r)));
|
||||
const promises = children
|
||||
.filter(child => child !== '.git')
|
||||
.map(child => this.openRepository(path.join(root, child)));
|
||||
|
||||
const folderConfig = workspace.getConfiguration('git', folder.uri);
|
||||
const paths = folderConfig.get<string[]>('scanRepositories') || [];
|
||||
|
||||
for (const possibleRepositoryPath of paths) {
|
||||
if (path.isAbsolute(possibleRepositoryPath)) {
|
||||
console.warn(localize('not supported', "Absolute paths not supported in 'git.scanRepositories' setting."));
|
||||
continue;
|
||||
}
|
||||
|
||||
promises.push(this.openRepository(path.join(root, possibleRepositoryPath)));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}));
|
||||
}
|
||||
|
||||
private onPossibleGitRepositoryChange(uri: Uri): void {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
|
||||
|
||||
if (autoRepositoryDetection === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.eventuallyScanPossibleGitRepository(uri.fsPath.replace(/\.git.*$/, ''));
|
||||
}
|
||||
|
||||
private eventuallyScanPossibleGitRepository(path: string) {
|
||||
this.possibleGitRepositoryPaths.add(path);
|
||||
this.eventuallyScanPossibleGitRepositories();
|
||||
}
|
||||
|
||||
@debounce(500)
|
||||
private eventuallyScanPossibleGitRepositories(): void {
|
||||
for (const path of this.possibleGitRepositoryPaths) {
|
||||
this.openRepository(path);
|
||||
}
|
||||
|
||||
this.possibleGitRepositoryPaths.clear();
|
||||
}
|
||||
|
||||
private async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise<void> {
|
||||
const possibleRepositoryFolders = added
|
||||
.filter(folder => !this.getOpenRepository(folder.uri));
|
||||
|
||||
const activeRepositoriesList = window.visibleTextEditors
|
||||
.map(editor => this.getRepository(editor.document.uri))
|
||||
.filter(repository => !!repository) as Repository[];
|
||||
|
||||
const activeRepositories = new Set<Repository>(activeRepositoriesList);
|
||||
const openRepositoriesToDispose = removed
|
||||
.map(folder => this.getOpenRepository(folder.uri))
|
||||
.filter(r => !!r)
|
||||
.filter(r => !activeRepositories.has(r!.repository))
|
||||
.filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[];
|
||||
|
||||
openRepositoriesToDispose.forEach(r => r.dispose());
|
||||
await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath)));
|
||||
}
|
||||
|
||||
private onDidChangeConfiguration(): void {
|
||||
const possibleRepositoryFolders = (workspace.workspaceFolders || [])
|
||||
.filter(folder => workspace.getConfiguration('git', folder.uri).get<boolean>('enabled') === true)
|
||||
.filter(folder => !this.getOpenRepository(folder.uri));
|
||||
|
||||
const openRepositoriesToDispose = this.openRepositories
|
||||
.map(repository => ({ repository, root: Uri.file(repository.repository.root) }))
|
||||
.filter(({ root }) => workspace.getConfiguration('git', root).get<boolean>('enabled') !== true)
|
||||
.map(({ repository }) => repository);
|
||||
|
||||
possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath));
|
||||
openRepositoriesToDispose.forEach(r => r.dispose());
|
||||
}
|
||||
|
||||
private async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise<void> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
|
||||
|
||||
if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(editors.map(async editor => {
|
||||
const uri = editor.document.uri;
|
||||
|
||||
if (uri.scheme !== 'file') {
|
||||
return;
|
||||
}
|
||||
|
||||
const repository = this.getRepository(uri);
|
||||
|
||||
if (repository) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.openRepository(path.dirname(uri.fsPath));
|
||||
}));
|
||||
}
|
||||
|
||||
@sequentialize
|
||||
async openRepository(path: string): Promise<void> {
|
||||
if (this.getRepository(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = workspace.getConfiguration('git', Uri.file(path));
|
||||
const enabled = config.get<boolean>('enabled') === true;
|
||||
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const rawRoot = await this.git.getRepositoryRoot(path);
|
||||
|
||||
// This can happen whenever `path` has the wrong case sensitivity in
|
||||
// case insensitive file systems
|
||||
// https://github.com/microsoft/vscode/issues/33498
|
||||
const repositoryRoot = Uri.file(rawRoot).fsPath;
|
||||
|
||||
if (this.getRepository(repositoryRoot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.shouldRepositoryBeIgnored(rawRoot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dotGit = await this.git.getRepositoryDotGit(repositoryRoot);
|
||||
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel);
|
||||
|
||||
this.open(repository);
|
||||
await repository.status();
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
private shouldRepositoryBeIgnored(repositoryRoot: string): boolean {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const ignoredRepos = config.get<string[]>('ignoredRepositories') || [];
|
||||
|
||||
for (const ignoredRepo of ignoredRepos) {
|
||||
if (path.isAbsolute(ignoredRepo)) {
|
||||
if (pathEquals(ignoredRepo, repositoryRoot)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for (const folder of workspace.workspaceFolders || []) {
|
||||
if (pathEquals(path.join(folder.uri.fsPath, ignoredRepo), repositoryRoot)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private open(repository: Repository): void {
|
||||
this.outputChannel.appendLine(`Open repository: ${repository.root}`);
|
||||
|
||||
const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);
|
||||
const disappearListener = onDidDisappearRepository(() => dispose());
|
||||
const changeListener = repository.onDidChangeRepository(uri => this._onDidChangeRepository.fire({ repository, uri }));
|
||||
const originalResourceChangeListener = repository.onDidChangeOriginalResource(uri => this._onDidChangeOriginalResource.fire({ repository, uri }));
|
||||
|
||||
const shouldDetectSubmodules = workspace
|
||||
.getConfiguration('git', Uri.file(repository.root))
|
||||
.get<boolean>('detectSubmodules') as boolean;
|
||||
|
||||
const submodulesLimit = workspace
|
||||
.getConfiguration('git', Uri.file(repository.root))
|
||||
.get<number>('detectSubmodulesLimit') as number;
|
||||
|
||||
const checkForSubmodules = () => {
|
||||
if (!shouldDetectSubmodules) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (repository.submodules.length > submodulesLimit) {
|
||||
window.showWarningMessage(localize('too many submodules', "The '{0}' repository has {1} submodules which won't be opened automatically. You can still open each one individually by opening a file within.", path.basename(repository.root), repository.submodules.length));
|
||||
statusListener.dispose();
|
||||
}
|
||||
|
||||
repository.submodules
|
||||
.slice(0, submodulesLimit)
|
||||
.map(r => path.join(repository.root, r.path))
|
||||
.forEach(p => this.eventuallyScanPossibleGitRepository(p));
|
||||
};
|
||||
|
||||
const statusListener = repository.onDidRunGitStatus(checkForSubmodules);
|
||||
checkForSubmodules();
|
||||
|
||||
const dispose = () => {
|
||||
disappearListener.dispose();
|
||||
changeListener.dispose();
|
||||
originalResourceChangeListener.dispose();
|
||||
statusListener.dispose();
|
||||
repository.dispose();
|
||||
|
||||
this.openRepositories = this.openRepositories.filter(e => e !== openRepository);
|
||||
this._onDidCloseRepository.fire(repository);
|
||||
};
|
||||
|
||||
const openRepository = { repository, dispose };
|
||||
this.openRepositories.push(openRepository);
|
||||
this._onDidOpenRepository.fire(repository);
|
||||
}
|
||||
|
||||
close(repository: Repository): void {
|
||||
const openRepository = this.getOpenRepository(repository);
|
||||
|
||||
if (!openRepository) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.outputChannel.appendLine(`Close repository: ${repository.root}`);
|
||||
openRepository.dispose();
|
||||
}
|
||||
|
||||
async pickRepository(): Promise<Repository | undefined> {
|
||||
if (this.openRepositories.length === 0) {
|
||||
throw new Error(localize('no repositories', "There are no available repositories"));
|
||||
}
|
||||
|
||||
const picks = this.openRepositories.map((e, index) => new RepositoryPick(e.repository, index));
|
||||
const active = window.activeTextEditor;
|
||||
const repository = active && this.getRepository(active.document.fileName);
|
||||
const index = picks.findIndex(pick => pick.repository === repository);
|
||||
|
||||
// Move repository pick containing the active text editor to appear first
|
||||
if (index > -1) {
|
||||
picks.unshift(...picks.splice(index, 1));
|
||||
}
|
||||
|
||||
const placeHolder = localize('pick repo', "Choose a repository");
|
||||
const pick = await window.showQuickPick(picks, { placeHolder });
|
||||
|
||||
return pick && pick.repository;
|
||||
}
|
||||
|
||||
getRepository(sourceControl: SourceControl): Repository | undefined;
|
||||
getRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined;
|
||||
getRepository(path: string): Repository | undefined;
|
||||
getRepository(resource: Uri): Repository | undefined;
|
||||
getRepository(hint: any): Repository | undefined {
|
||||
const liveRepository = this.getOpenRepository(hint);
|
||||
return liveRepository && liveRepository.repository;
|
||||
}
|
||||
|
||||
private getOpenRepository(repository: Repository): OpenRepository | undefined;
|
||||
private getOpenRepository(sourceControl: SourceControl): OpenRepository | undefined;
|
||||
private getOpenRepository(resourceGroup: SourceControlResourceGroup): OpenRepository | undefined;
|
||||
private getOpenRepository(path: string): OpenRepository | undefined;
|
||||
private getOpenRepository(resource: Uri): OpenRepository | undefined;
|
||||
private getOpenRepository(hint: any): OpenRepository | undefined {
|
||||
if (!hint) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (hint instanceof Repository) {
|
||||
return this.openRepositories.filter(r => r.repository === hint)[0];
|
||||
}
|
||||
|
||||
if (typeof hint === 'string') {
|
||||
hint = Uri.file(hint);
|
||||
}
|
||||
|
||||
if (hint instanceof Uri) {
|
||||
let resourcePath: string;
|
||||
|
||||
if (hint.scheme === 'git') {
|
||||
resourcePath = fromGitUri(hint).path;
|
||||
} else {
|
||||
resourcePath = hint.fsPath;
|
||||
}
|
||||
|
||||
outer:
|
||||
for (const liveRepository of this.openRepositories.sort((a, b) => b.repository.root.length - a.repository.root.length)) {
|
||||
if (!isDescendant(liveRepository.repository.root, resourcePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const submodule of liveRepository.repository.submodules) {
|
||||
const submoduleRoot = path.join(liveRepository.repository.root, submodule.path);
|
||||
|
||||
if (isDescendant(submoduleRoot, resourcePath)) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
|
||||
return liveRepository;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (const liveRepository of this.openRepositories) {
|
||||
const repository = liveRepository.repository;
|
||||
|
||||
if (hint === repository.sourceControl) {
|
||||
return liveRepository;
|
||||
}
|
||||
|
||||
if (hint === repository.mergeGroup || hint === repository.indexGroup || hint === repository.workingTreeGroup) {
|
||||
return liveRepository;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getRepositoryForSubmodule(submoduleUri: Uri): Repository | undefined {
|
||||
for (const repository of this.repositories) {
|
||||
for (const submodule of repository.submodules) {
|
||||
const submodulePath = path.join(repository.root, submodule.path);
|
||||
|
||||
if (submodulePath === submoduleUri.fsPath) {
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
|
||||
this.remoteSourceProviders.add(provider);
|
||||
this._onDidAddRemoteSourceProvider.fire(provider);
|
||||
|
||||
return toDisposable(() => {
|
||||
this.remoteSourceProviders.delete(provider);
|
||||
this._onDidRemoveRemoteSourceProvider.fire(provider);
|
||||
});
|
||||
}
|
||||
|
||||
registerCredentialsProvider(provider: CredentialsProvider): Disposable {
|
||||
return this.askpass.registerCredentialsProvider(provider);
|
||||
}
|
||||
|
||||
getRemoteProviders(): RemoteSourceProvider[] {
|
||||
return [...this.remoteSourceProviders.values()];
|
||||
}
|
||||
|
||||
registerPushErrorHandler(handler: PushErrorHandler): Disposable {
|
||||
this.pushErrorHandlers.add(handler);
|
||||
return toDisposable(() => this.pushErrorHandlers.delete(handler));
|
||||
}
|
||||
|
||||
getPushErrorHandlers(): PushErrorHandler[] {
|
||||
return [...this.pushErrorHandlers];
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
const openRepositories = [...this.openRepositories];
|
||||
openRepositories.forEach(r => r.dispose());
|
||||
this.openRepositories = [];
|
||||
|
||||
this.possibleGitRepositoryPaths.clear();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
37
lib/vscode/extensions/git/src/protocolHandler.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { UriHandler, Uri, window, Disposable, commands } from 'vscode';
|
||||
import { dispose } from './util';
|
||||
import * as querystring from 'querystring';
|
||||
|
||||
export class GitProtocolHandler implements UriHandler {
|
||||
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor() {
|
||||
this.disposables.push(window.registerUriHandler(this));
|
||||
}
|
||||
|
||||
handleUri(uri: Uri): void {
|
||||
switch (uri.path) {
|
||||
case '/clone': this.clone(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private clone(uri: Uri): void {
|
||||
const data = querystring.parse(uri.query);
|
||||
|
||||
if (!data.url) {
|
||||
console.warn('Failed to open URI:', uri);
|
||||
}
|
||||
|
||||
commands.executeCommand('git.clone', data.url);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
12
lib/vscode/extensions/git/src/pushError.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vscode';
|
||||
import { PushErrorHandler } from './api/git';
|
||||
|
||||
export interface IPushErrorHandlerRegistry {
|
||||
registerPushErrorHandler(provider: PushErrorHandler): Disposable;
|
||||
getPushErrorHandlers(): PushErrorHandler[];
|
||||
}
|
14
lib/vscode/extensions/git/src/remoteProvider.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, Event } from 'vscode';
|
||||
import { RemoteSourceProvider } from './api/git';
|
||||
|
||||
export interface IRemoteSourceProviderRegistry {
|
||||
readonly onDidAddRemoteSourceProvider: Event<RemoteSourceProvider>;
|
||||
readonly onDidRemoveRemoteSourceProvider: Event<RemoteSourceProvider>;
|
||||
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
|
||||
getRemoteProviders(): RemoteSourceProvider[];
|
||||
}
|
133
lib/vscode/extensions/git/src/remoteSource.ts
Normal file
@ -0,0 +1,133 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { QuickPickItem, window, QuickPick } from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { RemoteSourceProvider, RemoteSource } from './api/git';
|
||||
import { Model } from './model';
|
||||
import { throttle, debounce } from './decorators';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
async function getQuickPickResult<T extends QuickPickItem>(quickpick: QuickPick<T>): Promise<T | undefined> {
|
||||
const result = await new Promise<T | undefined>(c => {
|
||||
quickpick.onDidAccept(() => c(quickpick.selectedItems[0]));
|
||||
quickpick.onDidHide(() => c(undefined));
|
||||
quickpick.show();
|
||||
});
|
||||
|
||||
quickpick.hide();
|
||||
return result;
|
||||
}
|
||||
|
||||
class RemoteSourceProviderQuickPick {
|
||||
|
||||
private quickpick: QuickPick<QuickPickItem & { remoteSource?: RemoteSource }>;
|
||||
|
||||
constructor(private provider: RemoteSourceProvider) {
|
||||
this.quickpick = window.createQuickPick();
|
||||
this.quickpick.ignoreFocusOut = true;
|
||||
|
||||
if (provider.supportsQuery) {
|
||||
this.quickpick.placeholder = localize('type to search', "Repository name (type to search)");
|
||||
this.quickpick.onDidChangeValue(this.onDidChangeValue, this);
|
||||
} else {
|
||||
this.quickpick.placeholder = localize('type to filter', "Repository name");
|
||||
}
|
||||
}
|
||||
|
||||
@debounce(300)
|
||||
private onDidChangeValue(): void {
|
||||
this.query();
|
||||
}
|
||||
|
||||
@throttle
|
||||
private async query(): Promise<void> {
|
||||
this.quickpick.busy = true;
|
||||
|
||||
try {
|
||||
const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || [];
|
||||
|
||||
if (remoteSources.length === 0) {
|
||||
this.quickpick.items = [{
|
||||
label: localize('none found', "No remote repositories found."),
|
||||
alwaysShow: true
|
||||
}];
|
||||
} else {
|
||||
this.quickpick.items = remoteSources.map(remoteSource => ({
|
||||
label: remoteSource.name,
|
||||
description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]),
|
||||
remoteSource
|
||||
}));
|
||||
}
|
||||
} catch (err) {
|
||||
this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }];
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.quickpick.busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
async pick(): Promise<RemoteSource | undefined> {
|
||||
this.query();
|
||||
const result = await getQuickPickResult(this.quickpick);
|
||||
return result?.remoteSource;
|
||||
}
|
||||
}
|
||||
|
||||
export interface PickRemoteSourceOptions {
|
||||
readonly providerLabel?: (provider: RemoteSourceProvider) => string;
|
||||
readonly urlLabel?: string;
|
||||
}
|
||||
|
||||
export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise<string | undefined> {
|
||||
const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>();
|
||||
quickpick.ignoreFocusOut = true;
|
||||
|
||||
const providers = model.getRemoteProviders()
|
||||
.map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider }));
|
||||
|
||||
quickpick.placeholder = providers.length === 0
|
||||
? localize('provide url', "Provide repository URL")
|
||||
: localize('provide url or pick', "Provide repository URL or pick a repository source.");
|
||||
|
||||
const updatePicks = (value?: string) => {
|
||||
if (value) {
|
||||
quickpick.items = [{
|
||||
label: options.urlLabel ?? localize('url', "URL"),
|
||||
description: value,
|
||||
alwaysShow: true,
|
||||
url: value
|
||||
},
|
||||
...providers];
|
||||
} else {
|
||||
quickpick.items = providers;
|
||||
}
|
||||
};
|
||||
|
||||
quickpick.onDidChangeValue(updatePicks);
|
||||
updatePicks();
|
||||
|
||||
const result = await getQuickPickResult(quickpick);
|
||||
|
||||
if (result) {
|
||||
if (result.url) {
|
||||
return result.url;
|
||||
} else if (result.provider) {
|
||||
const quickpick = new RemoteSourceProviderQuickPick(result.provider);
|
||||
const remote = await quickpick.pick();
|
||||
|
||||
if (remote) {
|
||||
if (typeof remote.url === 'string') {
|
||||
return remote.url;
|
||||
} else if (remote.url.length > 0) {
|
||||
return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
1831
lib/vscode/extensions/git/src/repository.ts
Normal file
128
lib/vscode/extensions/git/src/staging.ts
Normal file
@ -0,0 +1,128 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TextDocument, Range, LineChange, Selection } from 'vscode';
|
||||
|
||||
export function applyLineChanges(original: TextDocument, modified: TextDocument, diffs: LineChange[]): string {
|
||||
const result: string[] = [];
|
||||
let currentLine = 0;
|
||||
|
||||
for (let diff of diffs) {
|
||||
const isInsertion = diff.originalEndLineNumber === 0;
|
||||
const isDeletion = diff.modifiedEndLineNumber === 0;
|
||||
|
||||
let endLine = isInsertion ? diff.originalStartLineNumber : diff.originalStartLineNumber - 1;
|
||||
let endCharacter = 0;
|
||||
|
||||
// if this is a deletion at the very end of the document,then we need to account
|
||||
// for a newline at the end of the last line which may have been deleted
|
||||
// https://github.com/microsoft/vscode/issues/59670
|
||||
if (isDeletion && diff.originalEndLineNumber === original.lineCount) {
|
||||
endLine -= 1;
|
||||
endCharacter = original.lineAt(endLine).range.end.character;
|
||||
}
|
||||
|
||||
result.push(original.getText(new Range(currentLine, 0, endLine, endCharacter)));
|
||||
|
||||
if (!isDeletion) {
|
||||
let fromLine = diff.modifiedStartLineNumber - 1;
|
||||
let fromCharacter = 0;
|
||||
|
||||
// if this is an insertion at the very end of the document,
|
||||
// then we must start the next range after the last character of the
|
||||
// previous line, in order to take the correct eol
|
||||
if (isInsertion && diff.originalStartLineNumber === original.lineCount) {
|
||||
fromLine -= 1;
|
||||
fromCharacter = modified.lineAt(fromLine).range.end.character;
|
||||
}
|
||||
|
||||
result.push(modified.getText(new Range(fromLine, fromCharacter, diff.modifiedEndLineNumber, 0)));
|
||||
}
|
||||
|
||||
currentLine = isInsertion ? diff.originalStartLineNumber : diff.originalEndLineNumber;
|
||||
}
|
||||
|
||||
result.push(original.getText(new Range(currentLine, 0, original.lineCount, 0)));
|
||||
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
export function toLineRanges(selections: Selection[], textDocument: TextDocument): Range[] {
|
||||
const lineRanges = selections.map(s => {
|
||||
const startLine = textDocument.lineAt(s.start.line);
|
||||
const endLine = textDocument.lineAt(s.end.line);
|
||||
return new Range(startLine.range.start, endLine.range.end);
|
||||
});
|
||||
|
||||
lineRanges.sort((a, b) => a.start.line - b.start.line);
|
||||
|
||||
const result = lineRanges.reduce((result, l) => {
|
||||
if (result.length === 0) {
|
||||
result.push(l);
|
||||
return result;
|
||||
}
|
||||
|
||||
const [last, ...rest] = result;
|
||||
const intersection = l.intersection(last);
|
||||
|
||||
if (intersection) {
|
||||
return [intersection, ...rest];
|
||||
}
|
||||
|
||||
if (l.start.line === last.end.line + 1) {
|
||||
const merge = new Range(last.start, l.end);
|
||||
return [merge, ...rest];
|
||||
}
|
||||
|
||||
return [l, ...result];
|
||||
}, [] as Range[]);
|
||||
|
||||
result.reverse();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getModifiedRange(textDocument: TextDocument, diff: LineChange): Range {
|
||||
if (diff.modifiedEndLineNumber === 0) {
|
||||
if (diff.modifiedStartLineNumber === 0) {
|
||||
return new Range(textDocument.lineAt(diff.modifiedStartLineNumber).range.end, textDocument.lineAt(diff.modifiedStartLineNumber).range.start);
|
||||
} else if (textDocument.lineCount === diff.modifiedStartLineNumber) {
|
||||
return new Range(textDocument.lineAt(diff.modifiedStartLineNumber - 1).range.end, textDocument.lineAt(diff.modifiedStartLineNumber - 1).range.end);
|
||||
} else {
|
||||
return new Range(textDocument.lineAt(diff.modifiedStartLineNumber - 1).range.end, textDocument.lineAt(diff.modifiedStartLineNumber).range.start);
|
||||
}
|
||||
} else {
|
||||
return new Range(textDocument.lineAt(diff.modifiedStartLineNumber - 1).range.start, textDocument.lineAt(diff.modifiedEndLineNumber - 1).range.end);
|
||||
}
|
||||
}
|
||||
|
||||
export function intersectDiffWithRange(textDocument: TextDocument, diff: LineChange, range: Range): LineChange | null {
|
||||
const modifiedRange = getModifiedRange(textDocument, diff);
|
||||
const intersection = range.intersection(modifiedRange);
|
||||
|
||||
if (!intersection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (diff.modifiedEndLineNumber === 0) {
|
||||
return diff;
|
||||
} else {
|
||||
return {
|
||||
originalStartLineNumber: diff.originalStartLineNumber,
|
||||
originalEndLineNumber: diff.originalEndLineNumber,
|
||||
modifiedStartLineNumber: intersection.start.line + 1,
|
||||
modifiedEndLineNumber: intersection.end.line + 1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function invertLineChange(diff: LineChange): LineChange {
|
||||
return {
|
||||
modifiedStartLineNumber: diff.originalStartLineNumber,
|
||||
modifiedEndLineNumber: diff.originalEndLineNumber,
|
||||
originalStartLineNumber: diff.modifiedStartLineNumber,
|
||||
originalEndLineNumber: diff.modifiedEndLineNumber
|
||||
};
|
||||
}
|
207
lib/vscode/extensions/git/src/statusbar.ts
Normal file
@ -0,0 +1,207 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, Command, EventEmitter, Event, workspace, Uri } from 'vscode';
|
||||
import { Repository, Operation } from './repository';
|
||||
import { anyEvent, dispose, filterEvent } from './util';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Branch, RemoteSourceProvider } from './api/git';
|
||||
import { IRemoteSourceProviderRegistry } from './remoteProvider';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
class CheckoutStatusBar {
|
||||
|
||||
private _onDidChange = new EventEmitter<void>();
|
||||
get onDidChange(): Event<void> { return this._onDidChange.event; }
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(private repository: Repository) {
|
||||
repository.onDidRunGitStatus(this._onDidChange.fire, this._onDidChange, this.disposables);
|
||||
}
|
||||
|
||||
get command(): Command | undefined {
|
||||
const rebasing = !!this.repository.rebaseCommit;
|
||||
const title = `$(git-branch) ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}`;
|
||||
|
||||
return {
|
||||
command: 'git.checkout',
|
||||
tooltip: localize('checkout', "Checkout branch/tag..."),
|
||||
title,
|
||||
arguments: [this.repository.sourceControl]
|
||||
};
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
}
|
||||
}
|
||||
|
||||
interface SyncStatusBarState {
|
||||
readonly enabled: boolean;
|
||||
readonly isSyncRunning: boolean;
|
||||
readonly hasRemotes: boolean;
|
||||
readonly HEAD: Branch | undefined;
|
||||
readonly remoteSourceProviders: RemoteSourceProvider[];
|
||||
}
|
||||
|
||||
class SyncStatusBar {
|
||||
|
||||
private _onDidChange = new EventEmitter<void>();
|
||||
get onDidChange(): Event<void> { return this._onDidChange.event; }
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
private _state: SyncStatusBarState;
|
||||
private get state() { return this._state; }
|
||||
private set state(state: SyncStatusBarState) {
|
||||
this._state = state;
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
constructor(private repository: Repository, private remoteSourceProviderRegistry: IRemoteSourceProviderRegistry) {
|
||||
this._state = {
|
||||
enabled: true,
|
||||
isSyncRunning: false,
|
||||
hasRemotes: false,
|
||||
HEAD: undefined,
|
||||
remoteSourceProviders: this.remoteSourceProviderRegistry.getRemoteProviders()
|
||||
.filter(p => !!p.publishRepository)
|
||||
};
|
||||
|
||||
repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables);
|
||||
repository.onDidChangeOperations(this.onDidChangeOperations, this, this.disposables);
|
||||
|
||||
anyEvent(remoteSourceProviderRegistry.onDidAddRemoteSourceProvider, remoteSourceProviderRegistry.onDidRemoveRemoteSourceProvider)
|
||||
(this.onDidChangeRemoteSourceProviders, this, this.disposables);
|
||||
|
||||
const onEnablementChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.enableStatusBarSync'));
|
||||
onEnablementChange(this.updateEnablement, this, this.disposables);
|
||||
this.updateEnablement();
|
||||
}
|
||||
|
||||
private updateEnablement(): void {
|
||||
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
|
||||
const enabled = config.get<boolean>('enableStatusBarSync', true);
|
||||
|
||||
this.state = { ... this.state, enabled };
|
||||
}
|
||||
|
||||
private onDidChangeOperations(): void {
|
||||
const isSyncRunning = this.repository.operations.isRunning(Operation.Sync) ||
|
||||
this.repository.operations.isRunning(Operation.Push) ||
|
||||
this.repository.operations.isRunning(Operation.Pull);
|
||||
|
||||
this.state = { ...this.state, isSyncRunning };
|
||||
}
|
||||
|
||||
private onDidRunGitStatus(): void {
|
||||
this.state = {
|
||||
...this.state,
|
||||
hasRemotes: this.repository.remotes.length > 0,
|
||||
HEAD: this.repository.HEAD
|
||||
};
|
||||
}
|
||||
|
||||
private onDidChangeRemoteSourceProviders(): void {
|
||||
this.state = {
|
||||
...this.state,
|
||||
remoteSourceProviders: this.remoteSourceProviderRegistry.getRemoteProviders()
|
||||
.filter(p => !!p.publishRepository)
|
||||
};
|
||||
}
|
||||
|
||||
get command(): Command | undefined {
|
||||
if (!this.state.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.state.hasRemotes) {
|
||||
if (this.state.remoteSourceProviders.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tooltip = this.state.remoteSourceProviders.length === 1
|
||||
? localize('publish to', "Publish to {0}", this.state.remoteSourceProviders[0].name)
|
||||
: localize('publish to...', "Publish to...");
|
||||
|
||||
return {
|
||||
command: 'git.publish',
|
||||
title: `$(cloud-upload)`,
|
||||
tooltip,
|
||||
arguments: [this.repository.sourceControl]
|
||||
};
|
||||
}
|
||||
|
||||
const HEAD = this.state.HEAD;
|
||||
let icon = '$(sync)';
|
||||
let text = '';
|
||||
let command = '';
|
||||
let tooltip = '';
|
||||
|
||||
if (HEAD && HEAD.name && HEAD.commit) {
|
||||
if (HEAD.upstream) {
|
||||
if (HEAD.ahead || HEAD.behind) {
|
||||
text += this.repository.syncLabel;
|
||||
}
|
||||
|
||||
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
|
||||
const rebaseWhenSync = config.get<string>('rebaseWhenSync');
|
||||
|
||||
command = rebaseWhenSync ? 'git.syncRebase' : 'git.sync';
|
||||
tooltip = this.repository.syncTooltip;
|
||||
} else {
|
||||
icon = '$(cloud-upload)';
|
||||
command = 'git.publish';
|
||||
tooltip = localize('publish changes', "Publish Changes");
|
||||
}
|
||||
} else {
|
||||
command = '';
|
||||
tooltip = '';
|
||||
}
|
||||
|
||||
if (this.state.isSyncRunning) {
|
||||
icon = '$(sync~spin)';
|
||||
command = '';
|
||||
tooltip = localize('syncing changes', "Synchronizing Changes...");
|
||||
}
|
||||
|
||||
return {
|
||||
command,
|
||||
title: [icon, text].join(' ').trim(),
|
||||
tooltip,
|
||||
arguments: [this.repository.sourceControl]
|
||||
};
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.forEach(d => d.dispose());
|
||||
}
|
||||
}
|
||||
|
||||
export class StatusBarCommands {
|
||||
|
||||
readonly onDidChange: Event<void>;
|
||||
|
||||
private syncStatusBar: SyncStatusBar;
|
||||
private checkoutStatusBar: CheckoutStatusBar;
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(repository: Repository, remoteSourceProviderRegistry: IRemoteSourceProviderRegistry) {
|
||||
this.syncStatusBar = new SyncStatusBar(repository, remoteSourceProviderRegistry);
|
||||
this.checkoutStatusBar = new CheckoutStatusBar(repository);
|
||||
this.onDidChange = anyEvent(this.syncStatusBar.onDidChange, this.checkoutStatusBar.onDidChange);
|
||||
}
|
||||
|
||||
get commands(): Command[] {
|
||||
return [this.checkoutStatusBar.command, this.syncStatusBar.command]
|
||||
.filter((c): c is Command => !!c);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.syncStatusBar.dispose();
|
||||
this.checkoutStatusBar.dispose();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
44
lib/vscode/extensions/git/src/terminal.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ExtensionContext, workspace } from 'vscode';
|
||||
import { filterEvent, IDisposable } from './util';
|
||||
|
||||
export class TerminalEnvironmentManager {
|
||||
|
||||
private readonly disposable: IDisposable;
|
||||
|
||||
private _enabled = false;
|
||||
private set enabled(enabled: boolean) {
|
||||
if (this._enabled === enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._enabled = enabled;
|
||||
this.context.environmentVariableCollection.clear();
|
||||
|
||||
if (enabled) {
|
||||
for (const name of Object.keys(this.env)) {
|
||||
this.context.environmentVariableCollection.replace(name, this.env[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private readonly context: ExtensionContext, private readonly env: { [key: string]: string }) {
|
||||
this.disposable = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git'))
|
||||
(this.refresh, this);
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
private refresh(): void {
|
||||
const config = workspace.getConfiguration('git', null);
|
||||
this.enabled = config.get<boolean>('enabled', true) && config.get('terminalAuthentication', true);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposable.dispose();
|
||||
}
|
||||
}
|
399
lib/vscode/extensions/git/src/test/git.test.ts
Normal file
@ -0,0 +1,399 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import { GitStatusParser, parseGitCommits, parseGitmodules, parseLsTree, parseLsFiles } from '../git';
|
||||
import * as assert from 'assert';
|
||||
import { splitInChunks } from '../util';
|
||||
|
||||
suite('git', () => {
|
||||
suite('GitStatusParser', () => {
|
||||
test('empty parser', () => {
|
||||
const parser = new GitStatusParser();
|
||||
assert.deepEqual(parser.status, []);
|
||||
});
|
||||
|
||||
test('empty parser 2', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('');
|
||||
assert.deepEqual(parser.status, []);
|
||||
});
|
||||
|
||||
test('simple', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('?? file.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('simple 2', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('?? file.txt\0');
|
||||
parser.update('?? file2.txt\0');
|
||||
parser.update('?? file3.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('empty lines', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('');
|
||||
parser.update('?? file.txt\0');
|
||||
parser.update('');
|
||||
parser.update('');
|
||||
parser.update('?? file2.txt\0');
|
||||
parser.update('');
|
||||
parser.update('?? file3.txt\0');
|
||||
parser.update('');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('combined', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('?? file.txt\0?? file2.txt\0?? file3.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('split 1', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('?? file.txt\0?? file2');
|
||||
parser.update('.txt\0?? file3.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('split 2', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('?? file.txt');
|
||||
parser.update('\0?? file2.txt\0?? file3.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('split 3', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('?? file.txt\0?? file2.txt\0?? file3.txt');
|
||||
parser.update('\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('rename', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('R newfile.txt\0file.txt\0?? file2.txt\0?? file3.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: 'newfile.txt', x: 'R', y: ' ' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('rename split', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('R newfile.txt\0fil');
|
||||
parser.update('e.txt\0?? file2.txt\0?? file3.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file.txt', rename: 'newfile.txt', x: 'R', y: ' ' },
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('rename split 3', () => {
|
||||
const parser = new GitStatusParser();
|
||||
parser.update('?? file2.txt\0R new');
|
||||
parser.update('file.txt\0fil');
|
||||
parser.update('e.txt\0?? file3.txt\0');
|
||||
assert.deepEqual(parser.status, [
|
||||
{ path: 'file2.txt', rename: undefined, x: '?', y: '?' },
|
||||
{ path: 'file.txt', rename: 'newfile.txt', x: 'R', y: ' ' },
|
||||
{ path: 'file3.txt', rename: undefined, x: '?', y: '?' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
suite('parseGitmodules', () => {
|
||||
test('empty', () => {
|
||||
assert.deepEqual(parseGitmodules(''), []);
|
||||
});
|
||||
|
||||
test('sample', () => {
|
||||
const sample = `[submodule "deps/spdlog"]
|
||||
path = deps/spdlog
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
`;
|
||||
|
||||
assert.deepEqual(parseGitmodules(sample), [
|
||||
{ name: 'deps/spdlog', path: 'deps/spdlog', url: 'https://github.com/gabime/spdlog.git' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('big', () => {
|
||||
const sample = `[submodule "deps/spdlog"]
|
||||
path = deps/spdlog
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
[submodule "deps/spdlog2"]
|
||||
path = deps/spdlog2
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
[submodule "deps/spdlog3"]
|
||||
path = deps/spdlog3
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
[submodule "deps/spdlog4"]
|
||||
path = deps/spdlog4
|
||||
url = https://github.com/gabime/spdlog4.git
|
||||
`;
|
||||
|
||||
assert.deepEqual(parseGitmodules(sample), [
|
||||
{ name: 'deps/spdlog', path: 'deps/spdlog', url: 'https://github.com/gabime/spdlog.git' },
|
||||
{ name: 'deps/spdlog2', path: 'deps/spdlog2', url: 'https://github.com/gabime/spdlog.git' },
|
||||
{ name: 'deps/spdlog3', path: 'deps/spdlog3', url: 'https://github.com/gabime/spdlog.git' },
|
||||
{ name: 'deps/spdlog4', path: 'deps/spdlog4', url: 'https://github.com/gabime/spdlog4.git' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('whitespace #74844', () => {
|
||||
const sample = `[submodule "deps/spdlog"]
|
||||
path = deps/spdlog
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
`;
|
||||
|
||||
assert.deepEqual(parseGitmodules(sample), [
|
||||
{ name: 'deps/spdlog', path: 'deps/spdlog', url: 'https://github.com/gabime/spdlog.git' }
|
||||
]);
|
||||
});
|
||||
|
||||
test('whitespace again #108371', () => {
|
||||
const sample = `[submodule "deps/spdlog"]
|
||||
path= deps/spdlog
|
||||
url=https://github.com/gabime/spdlog.git
|
||||
`;
|
||||
|
||||
assert.deepEqual(parseGitmodules(sample), [
|
||||
{ name: 'deps/spdlog', path: 'deps/spdlog', url: 'https://github.com/gabime/spdlog.git' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
suite('parseGitCommit', () => {
|
||||
test('single parent commit', function () {
|
||||
const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1
|
||||
John Doe
|
||||
john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
8e5a374372b8393906c7e380dbb09349c5385554
|
||||
This is a commit message.\x00`;
|
||||
|
||||
assert.deepEqual(parseGitCommits(GIT_OUTPUT_SINGLE_PARENT), [{
|
||||
hash: '52c293a05038d865604c2284aa8698bd087915a1',
|
||||
message: 'This is a commit message.',
|
||||
parents: ['8e5a374372b8393906c7e380dbb09349c5385554'],
|
||||
authorDate: new Date(1580811030000),
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
}]);
|
||||
});
|
||||
|
||||
test('multiple parent commits', function () {
|
||||
const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
|
||||
John Doe
|
||||
john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
|
||||
This is a commit message.\x00`;
|
||||
|
||||
assert.deepEqual(parseGitCommits(GIT_OUTPUT_MULTIPLE_PARENTS), [{
|
||||
hash: '52c293a05038d865604c2284aa8698bd087915a1',
|
||||
message: 'This is a commit message.',
|
||||
parents: ['8e5a374372b8393906c7e380dbb09349c5385554', 'df27d8c75b129ab9b178b386077da2822101b217'],
|
||||
authorDate: new Date(1580811030000),
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
}]);
|
||||
});
|
||||
|
||||
test('no parent commits', function () {
|
||||
const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
|
||||
John Doe
|
||||
john.doe@mail.com
|
||||
1580811030
|
||||
1580811031
|
||||
|
||||
This is a commit message.\x00`;
|
||||
|
||||
assert.deepEqual(parseGitCommits(GIT_OUTPUT_NO_PARENTS), [{
|
||||
hash: '52c293a05038d865604c2284aa8698bd087915a1',
|
||||
message: 'This is a commit message.',
|
||||
parents: [],
|
||||
authorDate: new Date(1580811030000),
|
||||
authorName: 'John Doe',
|
||||
authorEmail: 'john.doe@mail.com',
|
||||
commitDate: new Date(1580811031000),
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
suite('parseLsTree', function () {
|
||||
test('sample', function () {
|
||||
const input = `040000 tree 0274a81f8ee9ca3669295dc40f510bd2021d0043 - .vscode
|
||||
100644 blob 1d487c1817262e4f20efbfa1d04c18f51b0046f6 491570 Screen Shot 2018-06-01 at 14.48.05.png
|
||||
100644 blob 686c16e4f019b734655a2576ce8b98749a9ffdb9 764420 Screen Shot 2018-06-07 at 20.04.59.png
|
||||
100644 blob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 4 boom.txt
|
||||
100644 blob 86dc360dd25f13fa50ffdc8259e9653921f4f2b7 11 boomcaboom.txt
|
||||
100644 blob a68b14060589b16d7ac75f67b905c918c03c06eb 24 file.js
|
||||
100644 blob f7bcfb05af46850d780f88c069edcd57481d822d 201 file.md
|
||||
100644 blob ab8b86114a051f6490f1ec5e3141b9a632fb46b5 8 hello.js
|
||||
100644 blob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 4 what.js
|
||||
100644 blob be859e3f412fa86513cd8bebe8189d1ea1a3e46d 24 what.txt
|
||||
100644 blob 56ec42c9dc6fcf4534788f0fe34b36e09f37d085 261186 what.txt2`;
|
||||
|
||||
const output = parseLsTree(input);
|
||||
|
||||
assert.deepEqual(output, [
|
||||
{ mode: '040000', type: 'tree', object: '0274a81f8ee9ca3669295dc40f510bd2021d0043', size: '-', file: '.vscode' },
|
||||
{ mode: '100644', type: 'blob', object: '1d487c1817262e4f20efbfa1d04c18f51b0046f6', size: '491570', file: 'Screen Shot 2018-06-01 at 14.48.05.png' },
|
||||
{ mode: '100644', type: 'blob', object: '686c16e4f019b734655a2576ce8b98749a9ffdb9', size: '764420', file: 'Screen Shot 2018-06-07 at 20.04.59.png' },
|
||||
{ mode: '100644', type: 'blob', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', size: '4', file: 'boom.txt' },
|
||||
{ mode: '100644', type: 'blob', object: '86dc360dd25f13fa50ffdc8259e9653921f4f2b7', size: '11', file: 'boomcaboom.txt' },
|
||||
{ mode: '100644', type: 'blob', object: 'a68b14060589b16d7ac75f67b905c918c03c06eb', size: '24', file: 'file.js' },
|
||||
{ mode: '100644', type: 'blob', object: 'f7bcfb05af46850d780f88c069edcd57481d822d', size: '201', file: 'file.md' },
|
||||
{ mode: '100644', type: 'blob', object: 'ab8b86114a051f6490f1ec5e3141b9a632fb46b5', size: '8', file: 'hello.js' },
|
||||
{ mode: '100644', type: 'blob', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', size: '4', file: 'what.js' },
|
||||
{ mode: '100644', type: 'blob', object: 'be859e3f412fa86513cd8bebe8189d1ea1a3e46d', size: '24', file: 'what.txt' },
|
||||
{ mode: '100644', type: 'blob', object: '56ec42c9dc6fcf4534788f0fe34b36e09f37d085', size: '261186', file: 'what.txt2' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
suite('parseLsFiles', function () {
|
||||
test('sample', function () {
|
||||
const input = `100644 7a73a41bfdf76d6f793007240d80983a52f15f97 0 .vscode/settings.json
|
||||
100644 1d487c1817262e4f20efbfa1d04c18f51b0046f6 0 Screen Shot 2018-06-01 at 14.48.05.png
|
||||
100644 686c16e4f019b734655a2576ce8b98749a9ffdb9 0 Screen Shot 2018-06-07 at 20.04.59.png
|
||||
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0 boom.txt
|
||||
100644 86dc360dd25f13fa50ffdc8259e9653921f4f2b7 0 boomcaboom.txt
|
||||
100644 a68b14060589b16d7ac75f67b905c918c03c06eb 0 file.js
|
||||
100644 f7bcfb05af46850d780f88c069edcd57481d822d 0 file.md
|
||||
100644 ab8b86114a051f6490f1ec5e3141b9a632fb46b5 0 hello.js
|
||||
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0 what.js
|
||||
100644 be859e3f412fa86513cd8bebe8189d1ea1a3e46d 0 what.txt
|
||||
100644 56ec42c9dc6fcf4534788f0fe34b36e09f37d085 0 what.txt2`;
|
||||
|
||||
const output = parseLsFiles(input);
|
||||
|
||||
assert.deepEqual(output, [
|
||||
{ mode: '100644', object: '7a73a41bfdf76d6f793007240d80983a52f15f97', stage: '0', file: '.vscode/settings.json' },
|
||||
{ mode: '100644', object: '1d487c1817262e4f20efbfa1d04c18f51b0046f6', stage: '0', file: 'Screen Shot 2018-06-01 at 14.48.05.png' },
|
||||
{ mode: '100644', object: '686c16e4f019b734655a2576ce8b98749a9ffdb9', stage: '0', file: 'Screen Shot 2018-06-07 at 20.04.59.png' },
|
||||
{ mode: '100644', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', stage: '0', file: 'boom.txt' },
|
||||
{ mode: '100644', object: '86dc360dd25f13fa50ffdc8259e9653921f4f2b7', stage: '0', file: 'boomcaboom.txt' },
|
||||
{ mode: '100644', object: 'a68b14060589b16d7ac75f67b905c918c03c06eb', stage: '0', file: 'file.js' },
|
||||
{ mode: '100644', object: 'f7bcfb05af46850d780f88c069edcd57481d822d', stage: '0', file: 'file.md' },
|
||||
{ mode: '100644', object: 'ab8b86114a051f6490f1ec5e3141b9a632fb46b5', stage: '0', file: 'hello.js' },
|
||||
{ mode: '100644', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', stage: '0', file: 'what.js' },
|
||||
{ mode: '100644', object: 'be859e3f412fa86513cd8bebe8189d1ea1a3e46d', stage: '0', file: 'what.txt' },
|
||||
{ mode: '100644', object: '56ec42c9dc6fcf4534788f0fe34b36e09f37d085', stage: '0', file: 'what.txt2' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
suite('splitInChunks', () => {
|
||||
test('unit tests', function () {
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 6)],
|
||||
[['hello'], ['there'], ['cool'], ['stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 10)],
|
||||
[['hello', 'there'], ['cool', 'stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 12)],
|
||||
[['hello', 'there'], ['cool', 'stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 14)],
|
||||
[['hello', 'there', 'cool'], ['stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['hello', 'there', 'cool', 'stuff'], 2000)],
|
||||
[['hello', 'there', 'cool', 'stuff']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 1)],
|
||||
[['0'], ['01'], ['012'], ['0'], ['01'], ['012'], ['0'], ['01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 2)],
|
||||
[['0'], ['01'], ['012'], ['0'], ['01'], ['012'], ['0'], ['01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 3)],
|
||||
[['0', '01'], ['012'], ['0', '01'], ['012'], ['0', '01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 4)],
|
||||
[['0', '01'], ['012', '0'], ['01'], ['012', '0'], ['01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 5)],
|
||||
[['0', '01'], ['012', '0'], ['01', '012'], ['0', '01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 6)],
|
||||
[['0', '01', '012'], ['0', '01', '012'], ['0', '01', '012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 7)],
|
||||
[['0', '01', '012', '0'], ['01', '012', '0'], ['01', '012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 8)],
|
||||
[['0', '01', '012', '0'], ['01', '012', '0', '01'], ['012']]
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...splitInChunks(['0', '01', '012', '0', '01', '012', '0', '01', '012'], 9)],
|
||||
[['0', '01', '012', '0', '01'], ['012', '0', '01', '012']]
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
40
lib/vscode/extensions/git/src/test/index.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
const path = require('path');
|
||||
const testRunner = require('vscode/lib/testrunner');
|
||||
|
||||
const options: any = {
|
||||
ui: 'tdd',
|
||||
useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'),
|
||||
timeout: 60000
|
||||
};
|
||||
|
||||
// These integration tests is being run in multiple environments (electron, web, remote)
|
||||
// so we need to set the suite name based on the environment as the suite name is used
|
||||
// for the test results file name
|
||||
let suite = '';
|
||||
if (process.env.VSCODE_BROWSER) {
|
||||
suite = `${process.env.VSCODE_BROWSER} Browser Integration Git Tests`;
|
||||
} else if (process.env.REMOTE_VSCODE) {
|
||||
suite = 'Remote Integration Git Tests';
|
||||
} else {
|
||||
suite = 'Integration Git Tests';
|
||||
}
|
||||
|
||||
if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) {
|
||||
options.reporter = 'mocha-multi-reporters';
|
||||
options.reporterOptions = {
|
||||
reporterEnabled: 'spec, mocha-junit-reporter',
|
||||
mochaJunitReporterReporterOptions: {
|
||||
testsuitesTitle: `${suite} ${process.platform}`,
|
||||
mochaFile: path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${process.arch}-${suite.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
testRunner.configure(options);
|
||||
|
||||
export = testRunner;
|
127
lib/vscode/extensions/git/src/test/smoke.test.ts
Normal file
@ -0,0 +1,127 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'mocha';
|
||||
import * as assert from 'assert';
|
||||
import { workspace, commands, window, Uri, WorkspaceEdit, Range, TextDocument, extensions } from 'vscode';
|
||||
import * as cp from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { GitExtension, API, Repository, Status } from '../api/git';
|
||||
import { eventToPromise } from '../util';
|
||||
|
||||
suite('git smoke test', function () {
|
||||
const cwd = fs.realpathSync(workspace.workspaceFolders![0].uri.fsPath);
|
||||
|
||||
function file(relativePath: string) {
|
||||
return path.join(cwd, relativePath);
|
||||
}
|
||||
|
||||
function uri(relativePath: string) {
|
||||
return Uri.file(file(relativePath));
|
||||
}
|
||||
|
||||
async function open(relativePath: string) {
|
||||
const doc = await workspace.openTextDocument(uri(relativePath));
|
||||
await window.showTextDocument(doc);
|
||||
return doc;
|
||||
}
|
||||
|
||||
async function type(doc: TextDocument, text: string) {
|
||||
const edit = new WorkspaceEdit();
|
||||
const end = doc.lineAt(doc.lineCount - 1).range.end;
|
||||
edit.replace(doc.uri, new Range(end, end), text);
|
||||
await workspace.applyEdit(edit);
|
||||
}
|
||||
|
||||
let git: API;
|
||||
let repository: Repository;
|
||||
|
||||
suiteSetup(async function () {
|
||||
fs.writeFileSync(file('app.js'), 'hello', 'utf8');
|
||||
fs.writeFileSync(file('index.pug'), 'hello', 'utf8');
|
||||
cp.execSync('git init', { cwd });
|
||||
cp.execSync('git config user.name testuser', { cwd });
|
||||
cp.execSync('git config user.email monacotools@microsoft.com', { cwd });
|
||||
cp.execSync('git add .', { cwd });
|
||||
cp.execSync('git commit -m "initial commit"', { cwd });
|
||||
|
||||
// make sure git is activated
|
||||
const ext = extensions.getExtension<GitExtension>('vscode.git');
|
||||
await ext?.activate();
|
||||
git = ext!.exports.getAPI(1);
|
||||
|
||||
if (git.repositories.length === 0) {
|
||||
await eventToPromise(git.onDidOpenRepository);
|
||||
}
|
||||
|
||||
assert.equal(git.repositories.length, 1);
|
||||
assert.equal(fs.realpathSync(git.repositories[0].rootUri.fsPath), cwd);
|
||||
|
||||
repository = git.repositories[0];
|
||||
});
|
||||
|
||||
test('reflects working tree changes', async function () {
|
||||
await commands.executeCommand('workbench.view.scm');
|
||||
|
||||
const appjs = await open('app.js');
|
||||
await type(appjs, ' world');
|
||||
await appjs.save();
|
||||
await repository.status();
|
||||
assert.equal(repository.state.workingTreeChanges.length, 1);
|
||||
repository.state.workingTreeChanges.some(r => r.uri.path === appjs.uri.path && r.status === Status.MODIFIED);
|
||||
|
||||
fs.writeFileSync(file('newfile.txt'), '');
|
||||
const newfile = await open('newfile.txt');
|
||||
await type(newfile, 'hey there');
|
||||
await newfile.save();
|
||||
await repository.status();
|
||||
assert.equal(repository.state.workingTreeChanges.length, 2);
|
||||
repository.state.workingTreeChanges.some(r => r.uri.path === appjs.uri.path && r.status === Status.MODIFIED);
|
||||
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.uri.path && r.status === Status.UNTRACKED);
|
||||
});
|
||||
|
||||
test('opens diff editor', async function () {
|
||||
const appjs = uri('app.js');
|
||||
await commands.executeCommand('git.openChange', appjs);
|
||||
|
||||
assert(window.activeTextEditor);
|
||||
assert.equal(window.activeTextEditor!.document.uri.path, appjs.path);
|
||||
|
||||
// TODO: how do we really know this is a diff editor?
|
||||
});
|
||||
|
||||
test('stages correctly', async function () {
|
||||
const appjs = uri('app.js');
|
||||
const newfile = uri('newfile.txt');
|
||||
|
||||
await commands.executeCommand('git.stage', appjs);
|
||||
assert.equal(repository.state.workingTreeChanges.length, 1);
|
||||
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED);
|
||||
assert.equal(repository.state.indexChanges.length, 1);
|
||||
repository.state.indexChanges.some(r => r.uri.path === appjs.path && r.status === Status.INDEX_MODIFIED);
|
||||
|
||||
await commands.executeCommand('git.unstage', appjs);
|
||||
assert.equal(repository.state.workingTreeChanges.length, 2);
|
||||
repository.state.workingTreeChanges.some(r => r.uri.path === appjs.path && r.status === Status.MODIFIED);
|
||||
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED);
|
||||
});
|
||||
|
||||
test('stages, commits changes and verifies outgoing change', async function () {
|
||||
const appjs = uri('app.js');
|
||||
const newfile = uri('newfile.txt');
|
||||
|
||||
await commands.executeCommand('git.stage', appjs);
|
||||
await repository.commit('second commit');
|
||||
assert.equal(repository.state.workingTreeChanges.length, 1);
|
||||
repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED);
|
||||
assert.equal(repository.state.indexChanges.length, 0);
|
||||
|
||||
await commands.executeCommand('git.stageAll', appjs);
|
||||
await repository.commit('third commit');
|
||||
assert.equal(repository.state.workingTreeChanges.length, 0);
|
||||
assert.equal(repository.state.indexChanges.length, 0);
|
||||
});
|
||||
});
|
264
lib/vscode/extensions/git/src/timelineProvider.ts
Normal file
@ -0,0 +1,264 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { CancellationToken, ConfigurationChangeEvent, Disposable, env, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace } from 'vscode';
|
||||
import { Model } from './model';
|
||||
import { Repository, Resource } from './repository';
|
||||
import { debounce } from './decorators';
|
||||
import { emojify, ensureEmojis } from './emoji';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class GitTimelineItem extends TimelineItem {
|
||||
static is(item: TimelineItem): item is GitTimelineItem {
|
||||
return item instanceof GitTimelineItem;
|
||||
}
|
||||
|
||||
readonly ref: string;
|
||||
readonly previousRef: string;
|
||||
readonly message: string;
|
||||
|
||||
constructor(
|
||||
ref: string,
|
||||
previousRef: string,
|
||||
message: string,
|
||||
timestamp: number,
|
||||
id: string,
|
||||
contextValue: string
|
||||
) {
|
||||
const index = message.indexOf('\n');
|
||||
const label = index !== -1 ? `${message.substring(0, index)} \u2026` : message;
|
||||
|
||||
super(label, timestamp);
|
||||
|
||||
this.ref = ref;
|
||||
this.previousRef = previousRef;
|
||||
this.message = message;
|
||||
this.id = id;
|
||||
this.contextValue = contextValue;
|
||||
}
|
||||
|
||||
get shortRef() {
|
||||
return this.shortenRef(this.ref);
|
||||
}
|
||||
|
||||
get shortPreviousRef() {
|
||||
return this.shortenRef(this.previousRef);
|
||||
}
|
||||
|
||||
private shortenRef(ref: string): string {
|
||||
if (ref === '' || ref === '~' || ref === 'HEAD') {
|
||||
return ref;
|
||||
}
|
||||
return ref.endsWith('^') ? `${ref.substr(0, 8)}^` : ref.substr(0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
export class GitTimelineProvider implements TimelineProvider {
|
||||
private _onDidChange = new EventEmitter<TimelineChangeEvent>();
|
||||
get onDidChange(): Event<TimelineChangeEvent> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
readonly id = 'git-history';
|
||||
readonly label = localize('git.timeline.source', 'Git History');
|
||||
|
||||
private readonly disposable: Disposable;
|
||||
private providerDisposable: Disposable | undefined;
|
||||
|
||||
private repo: Repository | undefined;
|
||||
private repoDisposable: Disposable | undefined;
|
||||
private repoStatusDate: Date | undefined;
|
||||
|
||||
constructor(private readonly model: Model) {
|
||||
this.disposable = Disposable.from(
|
||||
model.onDidOpenRepository(this.onRepositoriesChanged, this),
|
||||
workspace.onDidChangeConfiguration(this.onConfigurationChanged, this)
|
||||
);
|
||||
|
||||
if (model.repositories.length) {
|
||||
this.ensureProviderRegistration();
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.providerDisposable?.dispose();
|
||||
this.disposable.dispose();
|
||||
}
|
||||
|
||||
async provideTimeline(uri: Uri, options: TimelineOptions, _token: CancellationToken): Promise<Timeline> {
|
||||
// console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
|
||||
|
||||
const repo = this.model.getRepository(uri);
|
||||
if (!repo) {
|
||||
this.repoDisposable?.dispose();
|
||||
this.repoStatusDate = undefined;
|
||||
this.repo = undefined;
|
||||
|
||||
return { items: [] };
|
||||
}
|
||||
|
||||
if (this.repo?.root !== repo.root) {
|
||||
this.repoDisposable?.dispose();
|
||||
|
||||
this.repo = repo;
|
||||
this.repoStatusDate = new Date();
|
||||
this.repoDisposable = Disposable.from(
|
||||
repo.onDidChangeRepository(uri => this.onRepositoryChanged(repo, uri)),
|
||||
repo.onDidRunGitStatus(() => this.onRepositoryStatusChanged(repo))
|
||||
);
|
||||
}
|
||||
|
||||
const config = workspace.getConfiguration('git.timeline');
|
||||
|
||||
// TODO@eamodio: Ensure that the uri is a file -- if not we could get the history of the repo?
|
||||
|
||||
let limit: number | undefined;
|
||||
if (options.limit !== undefined && typeof options.limit !== 'number') {
|
||||
try {
|
||||
const result = await this.model.git.exec(repo.root, ['rev-list', '--count', `${options.limit.id}..`, '--', uri.fsPath]);
|
||||
if (!result.exitCode) {
|
||||
// Ask for 2 more (1 for the limit commit and 1 for the next commit) than so we can determine if there are more commits
|
||||
limit = Number(result.stdout) + 2;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
limit = undefined;
|
||||
}
|
||||
} else {
|
||||
// If we are not getting everything, ask for 1 more than so we can determine if there are more commits
|
||||
limit = options.limit === undefined ? undefined : options.limit + 1;
|
||||
}
|
||||
|
||||
await ensureEmojis();
|
||||
|
||||
const commits = await repo.logFile(uri, {
|
||||
maxEntries: limit,
|
||||
hash: options.cursor,
|
||||
// sortByAuthorDate: true
|
||||
});
|
||||
|
||||
const paging = commits.length ? {
|
||||
cursor: limit === undefined ? undefined : (commits.length >= limit ? commits[commits.length - 1]?.hash : undefined)
|
||||
} : undefined;
|
||||
|
||||
// If we asked for an extra commit, strip it off
|
||||
if (limit !== undefined && commits.length >= limit) {
|
||||
commits.splice(commits.length - 1, 1);
|
||||
}
|
||||
|
||||
const dateFormatter = new Intl.DateTimeFormat(env.language, { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' });
|
||||
|
||||
const dateType = config.get<'committed' | 'authored'>('date');
|
||||
const showAuthor = config.get<boolean>('showAuthor');
|
||||
|
||||
const items = commits.map<GitTimelineItem>((c, i) => {
|
||||
const date = dateType === 'authored' ? c.authorDate : c.commitDate;
|
||||
|
||||
const message = emojify(c.message);
|
||||
|
||||
const item = new GitTimelineItem(c.hash, commits[i + 1]?.hash ?? `${c.hash}^`, message, date?.getTime() ?? 0, c.hash, 'git:file:commit');
|
||||
item.iconPath = new (ThemeIcon as any)('git-commit');
|
||||
if (showAuthor) {
|
||||
item.description = c.authorName;
|
||||
}
|
||||
item.detail = `${c.authorName} (${c.authorEmail}) — ${c.hash.substr(0, 8)}\n${dateFormatter.format(date)}\n\n${message}`;
|
||||
item.command = {
|
||||
title: 'Open Comparison',
|
||||
command: 'git.timeline.openDiff',
|
||||
arguments: [item, uri, this.id]
|
||||
};
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
if (options.cursor === undefined) {
|
||||
const you = localize('git.timeline.you', 'You');
|
||||
|
||||
const index = repo.indexGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath);
|
||||
if (index) {
|
||||
const date = this.repoStatusDate ?? new Date();
|
||||
|
||||
const item = new GitTimelineItem('~', 'HEAD', localize('git.timeline.stagedChanges', 'Staged Changes'), date.getTime(), 'index', 'git:file:index');
|
||||
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
|
||||
item.iconPath = new (ThemeIcon as any)('git-commit');
|
||||
item.description = '';
|
||||
item.detail = localize('git.timeline.detail', '{0} — {1}\n{2}\n\n{3}', you, localize('git.index', 'Index'), dateFormatter.format(date), Resource.getStatusText(index.type));
|
||||
item.command = {
|
||||
title: 'Open Comparison',
|
||||
command: 'git.timeline.openDiff',
|
||||
arguments: [item, uri, this.id]
|
||||
};
|
||||
|
||||
items.splice(0, 0, item);
|
||||
}
|
||||
|
||||
const working = repo.workingTreeGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath);
|
||||
if (working) {
|
||||
const date = new Date();
|
||||
|
||||
const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommited Changes'), date.getTime(), 'working', 'git:file:working');
|
||||
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
|
||||
item.iconPath = new (ThemeIcon as any)('git-commit');
|
||||
item.description = '';
|
||||
item.detail = localize('git.timeline.detail', '{0} — {1}\n{2}\n\n{3}', you, localize('git.workingTree', 'Working Tree'), dateFormatter.format(date), Resource.getStatusText(working.type));
|
||||
item.command = {
|
||||
title: 'Open Comparison',
|
||||
command: 'git.timeline.openDiff',
|
||||
arguments: [item, uri, this.id]
|
||||
};
|
||||
|
||||
items.splice(0, 0, item);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
items: items,
|
||||
paging: paging
|
||||
};
|
||||
}
|
||||
|
||||
private ensureProviderRegistration() {
|
||||
if (this.providerDisposable === undefined) {
|
||||
this.providerDisposable = workspace.registerTimelineProvider(['file', 'git', 'vscode-remote', 'gitlens-git'], this);
|
||||
}
|
||||
}
|
||||
|
||||
private onConfigurationChanged(e: ConfigurationChangeEvent) {
|
||||
if (e.affectsConfiguration('git.timeline.date') || e.affectsConfiguration('git.timeline.showAuthor')) {
|
||||
this.fireChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private onRepositoriesChanged(_repo: Repository) {
|
||||
// console.log(`GitTimelineProvider.onRepositoriesChanged`);
|
||||
|
||||
this.ensureProviderRegistration();
|
||||
|
||||
// TODO@eamodio: Being naive for now and just always refreshing each time there is a new repository
|
||||
this.fireChanged();
|
||||
}
|
||||
|
||||
private onRepositoryChanged(_repo: Repository, _uri: Uri) {
|
||||
// console.log(`GitTimelineProvider.onRepositoryChanged: uri=${uri.toString(true)}`);
|
||||
|
||||
this.fireChanged();
|
||||
}
|
||||
|
||||
private onRepositoryStatusChanged(_repo: Repository) {
|
||||
// console.log(`GitTimelineProvider.onRepositoryStatusChanged`);
|
||||
|
||||
// This is less than ideal, but for now just save the last time a status was run and use that as the timestamp for staged items
|
||||
this.repoStatusDate = new Date();
|
||||
|
||||
this.fireChanged();
|
||||
}
|
||||
|
||||
@debounce(500)
|
||||
private fireChanged() {
|
||||
this._onDidChange.fire(undefined);
|
||||
}
|
||||
}
|
8
lib/vscode/extensions/git/src/typings/refs.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
|
||||
/// <reference path="../../../types/lib.textEncoder.d.ts" />
|
53
lib/vscode/extensions/git/src/uri.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Uri } from 'vscode';
|
||||
|
||||
export interface GitUriParams {
|
||||
path: string;
|
||||
ref: string;
|
||||
submoduleOf?: string;
|
||||
}
|
||||
|
||||
export function isGitUri(uri: Uri): boolean {
|
||||
return /^git$/.test(uri.scheme);
|
||||
}
|
||||
|
||||
export function fromGitUri(uri: Uri): GitUriParams {
|
||||
return JSON.parse(uri.query);
|
||||
}
|
||||
|
||||
export interface GitUriOptions {
|
||||
replaceFileExtension?: boolean;
|
||||
submoduleOf?: string;
|
||||
}
|
||||
|
||||
// As a mitigation for extensions like ESLint showing warnings and errors
|
||||
// for git URIs, let's change the file extension of these uris to .git,
|
||||
// when `replaceFileExtension` is true.
|
||||
export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Uri {
|
||||
const params: GitUriParams = {
|
||||
path: uri.fsPath,
|
||||
ref
|
||||
};
|
||||
|
||||
if (options.submoduleOf) {
|
||||
params.submoduleOf = options.submoduleOf;
|
||||
}
|
||||
|
||||
let path = uri.path;
|
||||
|
||||
if (options.replaceFileExtension) {
|
||||
path = `${path}.git`;
|
||||
} else if (options.submoduleOf) {
|
||||
path = `${path}.diff`;
|
||||
}
|
||||
|
||||
return uri.with({
|
||||
scheme: 'git',
|
||||
path,
|
||||
query: JSON.stringify(params)
|
||||
});
|
||||
}
|
416
lib/vscode/extensions/git/src/util.ts
Normal file
@ -0,0 +1,416 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Disposable, EventEmitter } from 'vscode';
|
||||
import { dirname, sep } from 'path';
|
||||
import { Readable } from 'stream';
|
||||
import { promises as fs, createReadStream } from 'fs';
|
||||
import * as byline from 'byline';
|
||||
|
||||
export function log(...args: any[]): void {
|
||||
console.log.apply(console, ['git:', ...args]);
|
||||
}
|
||||
|
||||
export interface IDisposable {
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export function dispose<T extends IDisposable>(disposables: T[]): T[] {
|
||||
disposables.forEach(d => d.dispose());
|
||||
return [];
|
||||
}
|
||||
|
||||
export function toDisposable(dispose: () => void): IDisposable {
|
||||
return { dispose };
|
||||
}
|
||||
|
||||
export function combinedDisposable(disposables: IDisposable[]): IDisposable {
|
||||
return toDisposable(() => dispose(disposables));
|
||||
}
|
||||
|
||||
export const EmptyDisposable = toDisposable(() => null);
|
||||
|
||||
export function fireEvent<T>(event: Event<T>): Event<T> {
|
||||
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(_ => (listener as any).call(thisArgs), null, disposables);
|
||||
}
|
||||
|
||||
export function mapEvent<I, O>(event: Event<I>, map: (i: I) => O): Event<O> {
|
||||
return (listener: (e: O) => any, thisArgs?: any, disposables?: Disposable[]) => event(i => listener.call(thisArgs, map(i)), null, disposables);
|
||||
}
|
||||
|
||||
export function filterEvent<T>(event: Event<T>, filter: (e: T) => boolean): Event<T> {
|
||||
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
|
||||
}
|
||||
|
||||
export function anyEvent<T>(...events: Event<T>[]): Event<T> {
|
||||
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
|
||||
const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i))));
|
||||
|
||||
if (disposables) {
|
||||
disposables.push(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function done<T>(promise: Promise<T>): Promise<void> {
|
||||
return promise.then<void>(() => undefined);
|
||||
}
|
||||
|
||||
export function onceEvent<T>(event: Event<T>): Event<T> {
|
||||
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
|
||||
const result = event(e => {
|
||||
result.dispose();
|
||||
return listener.call(thisArgs, e);
|
||||
}, null, disposables);
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export function debounceEvent<T>(event: Event<T>, delay: number): Event<T> {
|
||||
return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => {
|
||||
let timer: NodeJS.Timer;
|
||||
return event(e => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => listener.call(thisArgs, e), delay);
|
||||
}, null, disposables);
|
||||
};
|
||||
}
|
||||
|
||||
export function eventToPromise<T>(event: Event<T>): Promise<T> {
|
||||
return new Promise<T>(c => onceEvent(event)(c));
|
||||
}
|
||||
|
||||
export function once(fn: (...args: any[]) => any): (...args: any[]) => any {
|
||||
let didRun = false;
|
||||
|
||||
return (...args) => {
|
||||
if (didRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
return fn(...args);
|
||||
};
|
||||
}
|
||||
|
||||
export function assign<T>(destination: T, ...sources: any[]): T {
|
||||
for (const source of sources) {
|
||||
Object.keys(source).forEach(key => (destination as any)[key] = source[key]);
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
export function uniqBy<T>(arr: T[], fn: (el: T) => string): T[] {
|
||||
const seen = Object.create(null);
|
||||
|
||||
return arr.filter(el => {
|
||||
const key = fn(el);
|
||||
|
||||
if (seen[key]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
seen[key] = true;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export function groupBy<T>(arr: T[], fn: (el: T) => string): { [key: string]: T[] } {
|
||||
return arr.reduce((result, el) => {
|
||||
const key = fn(el);
|
||||
result[key] = [...(result[key] || []), el];
|
||||
return result;
|
||||
}, Object.create(null));
|
||||
}
|
||||
|
||||
|
||||
export async function mkdirp(path: string, mode?: number): Promise<boolean> {
|
||||
const mkdir = async () => {
|
||||
try {
|
||||
await fs.mkdir(path, mode);
|
||||
} catch (err) {
|
||||
if (err.code === 'EEXIST') {
|
||||
const stat = await fs.stat(path);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(`'${path}' exists and is not a directory.`);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
// is root?
|
||||
if (path === dirname(path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
await mkdir();
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
await mkdirp(dirname(path), mode);
|
||||
await mkdir();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function uniqueFilter<T>(keyFn: (t: T) => string): (t: T) => boolean {
|
||||
const seen: { [key: string]: boolean; } = Object.create(null);
|
||||
|
||||
return element => {
|
||||
const key = keyFn(element);
|
||||
|
||||
if (seen[key]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
seen[key] = true;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
export function find<T>(array: T[], fn: (t: T) => boolean): T | undefined {
|
||||
let result: T | undefined = undefined;
|
||||
|
||||
array.some(e => {
|
||||
if (fn(e)) {
|
||||
result = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function grep(filename: string, pattern: RegExp): Promise<boolean> {
|
||||
return new Promise<boolean>((c, e) => {
|
||||
const fileStream = createReadStream(filename, { encoding: 'utf8' });
|
||||
const stream = byline(fileStream);
|
||||
stream.on('data', (line: string) => {
|
||||
if (pattern.test(line)) {
|
||||
fileStream.close();
|
||||
c(true);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', e);
|
||||
stream.on('end', () => c(false));
|
||||
});
|
||||
}
|
||||
|
||||
export function readBytes(stream: Readable, bytes: number): Promise<Buffer> {
|
||||
return new Promise<Buffer>((complete, error) => {
|
||||
let done = false;
|
||||
let buffer = Buffer.allocUnsafe(bytes);
|
||||
let bytesRead = 0;
|
||||
|
||||
stream.on('data', (data: Buffer) => {
|
||||
let bytesToRead = Math.min(bytes - bytesRead, data.length);
|
||||
data.copy(buffer, bytesRead, 0, bytesToRead);
|
||||
bytesRead += bytesToRead;
|
||||
|
||||
if (bytesRead === bytes) {
|
||||
(stream as any).destroy(); // Will trigger the close event eventually
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('error', (e: Error) => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on('close', () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
complete(buffer.slice(0, bytesRead));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const enum Encoding {
|
||||
UTF8 = 'utf8',
|
||||
UTF16be = 'utf16be',
|
||||
UTF16le = 'utf16le'
|
||||
}
|
||||
|
||||
export function detectUnicodeEncoding(buffer: Buffer): Encoding | null {
|
||||
if (buffer.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const b0 = buffer.readUInt8(0);
|
||||
const b1 = buffer.readUInt8(1);
|
||||
|
||||
if (b0 === 0xFE && b1 === 0xFF) {
|
||||
return Encoding.UTF16be;
|
||||
}
|
||||
|
||||
if (b0 === 0xFF && b1 === 0xFE) {
|
||||
return Encoding.UTF16le;
|
||||
}
|
||||
|
||||
if (buffer.length < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const b2 = buffer.readUInt8(2);
|
||||
|
||||
if (b0 === 0xEF && b1 === 0xBB && b2 === 0xBF) {
|
||||
return Encoding.UTF8;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function isWindowsPath(path: string): boolean {
|
||||
return /^[a-zA-Z]:\\/.test(path);
|
||||
}
|
||||
|
||||
export function isDescendant(parent: string, descendant: string): boolean {
|
||||
if (parent === descendant) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parent.charAt(parent.length - 1) !== sep) {
|
||||
parent += sep;
|
||||
}
|
||||
|
||||
// Windows is case insensitive
|
||||
if (isWindowsPath(parent)) {
|
||||
parent = parent.toLowerCase();
|
||||
descendant = descendant.toLowerCase();
|
||||
}
|
||||
|
||||
return descendant.startsWith(parent);
|
||||
}
|
||||
|
||||
export function pathEquals(a: string, b: string): boolean {
|
||||
// Windows is case insensitive
|
||||
if (isWindowsPath(a)) {
|
||||
a = a.toLowerCase();
|
||||
b = b.toLowerCase();
|
||||
}
|
||||
|
||||
return a === b;
|
||||
}
|
||||
|
||||
export function* splitInChunks(array: string[], maxChunkLength: number): IterableIterator<string[]> {
|
||||
let current: string[] = [];
|
||||
let length = 0;
|
||||
|
||||
for (const value of array) {
|
||||
let newLength = length + value.length;
|
||||
|
||||
if (newLength > maxChunkLength && current.length > 0) {
|
||||
yield current;
|
||||
current = [];
|
||||
newLength = value.length;
|
||||
}
|
||||
|
||||
current.push(value);
|
||||
length = newLength;
|
||||
}
|
||||
|
||||
if (current.length > 0) {
|
||||
yield current;
|
||||
}
|
||||
}
|
||||
|
||||
interface ILimitedTaskFactory<T> {
|
||||
factory: () => Promise<T>;
|
||||
c: (value: T | Promise<T>) => void;
|
||||
e: (error?: any) => void;
|
||||
}
|
||||
|
||||
export class Limiter<T> {
|
||||
|
||||
private runningPromises: number;
|
||||
private maxDegreeOfParalellism: number;
|
||||
private outstandingPromises: ILimitedTaskFactory<T>[];
|
||||
|
||||
constructor(maxDegreeOfParalellism: number) {
|
||||
this.maxDegreeOfParalellism = maxDegreeOfParalellism;
|
||||
this.outstandingPromises = [];
|
||||
this.runningPromises = 0;
|
||||
}
|
||||
|
||||
queue(factory: () => Promise<T>): Promise<T> {
|
||||
return new Promise<T>((c, e) => {
|
||||
this.outstandingPromises.push({ factory, c, e });
|
||||
this.consume();
|
||||
});
|
||||
}
|
||||
|
||||
private consume(): void {
|
||||
while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
|
||||
const iLimitedTask = this.outstandingPromises.shift()!;
|
||||
this.runningPromises++;
|
||||
|
||||
const promise = iLimitedTask.factory();
|
||||
promise.then(iLimitedTask.c, iLimitedTask.e);
|
||||
promise.then(() => this.consumed(), () => this.consumed());
|
||||
}
|
||||
}
|
||||
|
||||
private consumed(): void {
|
||||
this.runningPromises--;
|
||||
|
||||
if (this.outstandingPromises.length > 0) {
|
||||
this.consume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Completion<T> = { success: true, value: T } | { success: false, err: any };
|
||||
|
||||
export class PromiseSource<T> {
|
||||
|
||||
private _onDidComplete = new EventEmitter<Completion<T>>();
|
||||
|
||||
private _promise: Promise<T> | undefined;
|
||||
get promise(): Promise<T> {
|
||||
if (this._promise) {
|
||||
return this._promise;
|
||||
}
|
||||
|
||||
return eventToPromise(this._onDidComplete.event).then(completion => {
|
||||
if (completion.success) {
|
||||
return completion.value;
|
||||
} else {
|
||||
throw completion.err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resolve(value: T): void {
|
||||
if (!this._promise) {
|
||||
this._promise = Promise.resolve(value);
|
||||
this._onDidComplete.fire({ success: true, value });
|
||||
}
|
||||
}
|
||||
|
||||
reject(err: any): void {
|
||||
if (!this._promise) {
|
||||
this._promise = Promise.reject(err);
|
||||
this._onDidComplete.fire({ success: false, err });
|
||||
}
|
||||
}
|
||||
}
|
25
lib/vscode/extensions/git/src/watch.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, EventEmitter, Uri } from 'vscode';
|
||||
import { join } from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { IDisposable } from './util';
|
||||
|
||||
export interface IFileWatcher extends IDisposable {
|
||||
readonly event: Event<Uri>;
|
||||
}
|
||||
|
||||
export function watch(location: string): IFileWatcher {
|
||||
const dotGitWatcher = fs.watch(location);
|
||||
const onDotGitFileChangeEmitter = new EventEmitter<Uri>();
|
||||
dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string))));
|
||||
dotGitWatcher.on('error', err => console.error(err));
|
||||
|
||||
return new class implements IFileWatcher {
|
||||
event = onDotGitFileChangeEmitter.event;
|
||||
dispose() { dotGitWatcher.close(); }
|
||||
};
|
||||
}
|
160
lib/vscode/extensions/git/syntaxes/diff.tmLanguage.json
Normal file
@ -0,0 +1,160 @@
|
||||
{
|
||||
"information_for_contributors": [
|
||||
"This file has been converted from https://github.com/textmate/diff.tmbundle/blob/master/Syntaxes/Diff.plist",
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/textmate/diff.tmbundle/commit/0593bb775eab1824af97ef2172fd38822abd97d7",
|
||||
"name": "Diff",
|
||||
"scopeName": "source.diff",
|
||||
"patterns": [
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.separator.diff"
|
||||
}
|
||||
},
|
||||
"match": "^((\\*{15})|(={67})|(-{3}))$\\n?",
|
||||
"name": "meta.separator.diff"
|
||||
},
|
||||
{
|
||||
"match": "^\\d+(,\\d+)*(a|d|c)\\d+(,\\d+)*$\\n?",
|
||||
"name": "meta.diff.range.normal"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.range.diff"
|
||||
},
|
||||
"2": {
|
||||
"name": "meta.toc-list.line-number.diff"
|
||||
},
|
||||
"3": {
|
||||
"name": "punctuation.definition.range.diff"
|
||||
}
|
||||
},
|
||||
"match": "^(@@)\\s*(.+?)\\s*(@@)($\\n?)?",
|
||||
"name": "meta.diff.range.unified"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"3": {
|
||||
"name": "punctuation.definition.range.diff"
|
||||
},
|
||||
"4": {
|
||||
"name": "punctuation.definition.range.diff"
|
||||
},
|
||||
"6": {
|
||||
"name": "punctuation.definition.range.diff"
|
||||
},
|
||||
"7": {
|
||||
"name": "punctuation.definition.range.diff"
|
||||
}
|
||||
},
|
||||
"match": "^(((\\-{3}) .+ (\\-{4}))|((\\*{3}) .+ (\\*{4})))$\\n?",
|
||||
"name": "meta.diff.range.context"
|
||||
},
|
||||
{
|
||||
"match": "^diff --git a/.*$\\n?",
|
||||
"name": "meta.diff.header.git"
|
||||
},
|
||||
{
|
||||
"match": "^diff (-|\\S+\\s+\\S+).*$\\n?",
|
||||
"name": "meta.diff.header.command"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"4": {
|
||||
"name": "punctuation.definition.from-file.diff"
|
||||
},
|
||||
"6": {
|
||||
"name": "punctuation.definition.from-file.diff"
|
||||
},
|
||||
"7": {
|
||||
"name": "punctuation.definition.from-file.diff"
|
||||
}
|
||||
},
|
||||
"match": "(^(((-{3}) .+)|((\\*{3}) .+))$\\n?|^(={4}) .+(?= - ))",
|
||||
"name": "meta.diff.header.from-file"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"2": {
|
||||
"name": "punctuation.definition.to-file.diff"
|
||||
},
|
||||
"3": {
|
||||
"name": "punctuation.definition.to-file.diff"
|
||||
},
|
||||
"4": {
|
||||
"name": "punctuation.definition.to-file.diff"
|
||||
}
|
||||
},
|
||||
"match": "(^(\\+{3}) .+$\\n?| (-) .* (={4})$\\n?)",
|
||||
"name": "meta.diff.header.to-file"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"3": {
|
||||
"name": "punctuation.definition.inserted.diff"
|
||||
},
|
||||
"6": {
|
||||
"name": "punctuation.definition.inserted.diff"
|
||||
}
|
||||
},
|
||||
"match": "^(((>)( .*)?)|((\\+).*))$\\n?",
|
||||
"name": "markup.inserted.diff"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.changed.diff"
|
||||
}
|
||||
},
|
||||
"match": "^(!).*$\\n?",
|
||||
"name": "markup.changed.diff"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"3": {
|
||||
"name": "punctuation.definition.deleted.diff"
|
||||
},
|
||||
"6": {
|
||||
"name": "punctuation.definition.deleted.diff"
|
||||
}
|
||||
},
|
||||
"match": "^(((<)( .*)?)|((-).*))$\\n?",
|
||||
"name": "markup.deleted.diff"
|
||||
},
|
||||
{
|
||||
"begin": "^(#)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.diff"
|
||||
}
|
||||
},
|
||||
"comment": "Git produces unified diffs with embedded comments\"",
|
||||
"end": "\\n",
|
||||
"name": "comment.line.number-sign.diff"
|
||||
},
|
||||
{
|
||||
"match": "^index [0-9a-f]{7,40}\\.\\.[0-9a-f]{7,40}.*$\\n?",
|
||||
"name": "meta.diff.index.git"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.separator.key-value.diff"
|
||||
},
|
||||
"2": {
|
||||
"name": "meta.toc-list.file-name.diff"
|
||||
}
|
||||
},
|
||||
"match": "^Index(:) (.+)$\\n?",
|
||||
"name": "meta.diff.index"
|
||||
},
|
||||
{
|
||||
"match": "^Only in .*: .*$\\n?",
|
||||
"name": "meta.diff.only-in"
|
||||
}
|
||||
]
|
||||
}
|
141
lib/vscode/extensions/git/syntaxes/git-commit.tmLanguage.json
Normal file
@ -0,0 +1,141 @@
|
||||
{
|
||||
"information_for_contributors": [
|
||||
"This file has been converted from https://github.com/textmate/git.tmbundle/blob/master/Syntaxes/Git%20Commit%20Message.tmLanguage",
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/textmate/git.tmbundle/commit/93897a78c6e52bef13dadc0d4091d203c5facb40",
|
||||
"name": "Git Commit Message",
|
||||
"scopeName": "text.git-commit",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\A(?!# Please enter the commit message)",
|
||||
"end": "^(?=# Please enter the commit message)",
|
||||
"name": "meta.scope.message.git-commit",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\A(?=#)",
|
||||
"end": "^(?!#)",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comment"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "^(?!# Please enter the commit message)",
|
||||
"end": "^(?=# Please enter the commit message)",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "\\G",
|
||||
"end": "^(?!\\G)",
|
||||
"name": "meta.scope.subject.git-commit",
|
||||
"patterns": [
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "keyword.other.$2.git-commit"
|
||||
}
|
||||
},
|
||||
"match": "\\G((fixup|squash)!)\\s*"
|
||||
},
|
||||
{
|
||||
"match": ".{73,}$",
|
||||
"name": "invalid.illegal.line-too-long.git-commit"
|
||||
},
|
||||
{
|
||||
"match": ".{51,}$",
|
||||
"name": "invalid.deprecated.line-too-long.git-commit"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "^(?!# Please enter the commit message)",
|
||||
"end": "^(?=# Please enter the commit message)",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#comment"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"begin": "^(?=# Please enter the commit message)",
|
||||
"end": "\\z",
|
||||
"name": "meta.scope.metadata.git-commit",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#metadata"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"comment": {
|
||||
"begin": "^(#)",
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.git-commit"
|
||||
}
|
||||
},
|
||||
"end": "\\n",
|
||||
"name": "comment.line.number-sign.git-commit"
|
||||
},
|
||||
"metadata": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(?=^# Changes to be committed:)",
|
||||
"end": "(?!\\G)((?=^# \\w)|(?!^#))",
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "(^[ \\t]+)?(?=#)",
|
||||
"beginCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.whitespace.comment.leading.git-commit"
|
||||
}
|
||||
},
|
||||
"contentName": "comment.line.number-sign.git-commit",
|
||||
"end": "(?!\\G)^",
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\G#",
|
||||
"name": "punctuation.definition.comment.git-commit"
|
||||
},
|
||||
{
|
||||
"match": "((modified|renamed):.*)$\\n?",
|
||||
"name": "markup.changed.git-commit"
|
||||
},
|
||||
{
|
||||
"match": "(new file:.*)$\\n?",
|
||||
"name": "markup.inserted.git-commit"
|
||||
},
|
||||
{
|
||||
"match": "(deleted:.*)$\\n?",
|
||||
"name": "markup.deleted.git-commit"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"include": "#comment"
|
||||
},
|
||||
{
|
||||
"begin": "(?=diff\\ \\-\\-git)",
|
||||
"comment": "diff presented at the end of the commit message when using commit -v.",
|
||||
"contentName": "source.diff",
|
||||
"end": "\\z",
|
||||
"name": "meta.embedded.diff.git-commit",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "source.diff"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
{
|
||||
"information_for_contributors": [
|
||||
"This file has been converted from https://github.com/textmate/git.tmbundle/blob/master/Syntaxes/Git%20Rebase%20Message.tmLanguage",
|
||||
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
|
||||
"Once accepted there, we are happy to receive an update request."
|
||||
],
|
||||
"version": "https://github.com/textmate/git.tmbundle/commit/5870cf3f8abad3a6637bdf69250b5d2ded427dc4",
|
||||
"name": "Git Rebase Message",
|
||||
"scopeName": "text.git-rebase",
|
||||
"patterns": [
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.git-rebase"
|
||||
}
|
||||
},
|
||||
"match": "^\\s*(#).*$\\n?",
|
||||
"name": "comment.line.number-sign.git-rebase"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "support.function.git-rebase"
|
||||
},
|
||||
"2": {
|
||||
"name": "constant.sha.git-rebase"
|
||||
},
|
||||
"3": {
|
||||
"name": "meta.commit-message.git-rebase"
|
||||
}
|
||||
},
|
||||
"match": "^\\s*(pick|p|reword|r|edit|e|squash|s|fixup|f|drop|d)\\s+([0-9a-f]+)\\s+(.*)$",
|
||||
"name": "meta.commit-command.git-rebase"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "support.function.git-rebase"
|
||||
},
|
||||
"2": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "source.shell"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"match": "^\\s*(exec|x)\\s+(.*)$",
|
||||
"name": "meta.commit-command.git-rebase"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "support.function.git-rebase"
|
||||
}
|
||||
},
|
||||
"match": "^\\s*(break|b)\\s*$",
|
||||
"name": "meta.commit-command.git-rebase"
|
||||
}
|
||||
]
|
||||
}
|
10
lib/vscode/extensions/git/syntaxes/ignore.tmLanguage.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Ignore",
|
||||
"scopeName": "source.ignore",
|
||||
"patterns": [
|
||||
{
|
||||
"match": "^#.*",
|
||||
"name": "comment.line.number-sign.ignore"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
This is the summary line. It can't be too long.
|
||||
After I can write a much more detailed description without quite the same restrictions on length.
|
||||
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
# with '#' will be ignored, and an empty message aborts the commit.
|
||||
# On branch master
|
||||
# Your branch is up-to-date with 'origin/master'.
|
||||
#
|
||||
# Changes to be committed:
|
||||
# deleted: README.md
|
||||
# modified: index.less
|
||||
# new file: spec/COMMIT_EDITMSG
|
||||
#
|
@ -0,0 +1,7 @@
|
||||
diff --git a/helloworld.txt b/helloworld.txt
|
||||
index e4f37c4..557db03 100644
|
||||
--- a/helloworld.txt
|
||||
+++ b/helloworld.txt
|
||||
@@ -1 +1 @@
|
||||
-Hello world
|
||||
+Hello World
|
@ -0,0 +1,15 @@
|
||||
pick 1fc6c95 Patch A
|
||||
squash fa39187 Something to add to patch A
|
||||
pick 7b36971 Something to move before patch B
|
||||
pick 6b2481b Patch B
|
||||
fixup c619268 A fix for Patch B
|
||||
edit dd1475d Something I want to split
|
||||
reword 4ca2acc i cant' typ goods
|
||||
|
||||
# Commands:
|
||||
# p, pick = use commit
|
||||
# r, reword = use commit, but edit the commit message
|
||||
# e, edit = use commit, but stop for amending
|
||||
# s, squash = use commit, but meld into previous commit
|
||||
# f, fixup = like "squash", but discard this commit's log message
|
||||
# x, exec = run command (the rest of the line) using shell
|
@ -0,0 +1,255 @@
|
||||
[
|
||||
{
|
||||
"c": "This is the summary line. It can't be too long.",
|
||||
"t": "text.git-commit meta.scope.message.git-commit meta.scope.subject.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "After I can write a much more detailed description without quite the same restrictions on length.",
|
||||
"t": "text.git-commit meta.scope.message.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " Please enter the commit message for your changes. Lines starting",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " with '#' will be ignored, and an empty message aborts the commit.",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " On branch master",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " Your branch is up-to-date with 'origin/master'.",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " Changes to be committed:",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "\t",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "deleted: README.md",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit markup.deleted.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "markup.deleted: #CE9178",
|
||||
"light_plus": "markup.deleted: #A31515",
|
||||
"dark_vs": "markup.deleted: #CE9178",
|
||||
"light_vs": "markup.deleted: #A31515",
|
||||
"hc_black": "markup.deleted: #CE9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "\t",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "modified: index.less",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit markup.changed.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "markup.changed: #569CD6",
|
||||
"light_plus": "markup.changed: #0451A5",
|
||||
"dark_vs": "markup.changed: #569CD6",
|
||||
"light_vs": "markup.changed: #0451A5",
|
||||
"hc_black": "markup.changed: #569CD6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "\t",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "new file: spec/COMMIT_EDITMSG",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit markup.inserted.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "markup.inserted: #B5CEA8",
|
||||
"light_plus": "markup.inserted: #098658",
|
||||
"dark_vs": "markup.inserted: #B5CEA8",
|
||||
"light_vs": "markup.inserted: #098658",
|
||||
"hc_black": "markup.inserted: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit punctuation.definition.comment.git-commit",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,167 @@
|
||||
[
|
||||
{
|
||||
"c": "diff --git a/helloworld.txt b/helloworld.txt",
|
||||
"t": "source.diff meta.diff.header.git",
|
||||
"r": {
|
||||
"dark_plus": "meta.diff.header: #569CD6",
|
||||
"light_plus": "meta.diff.header: #000080",
|
||||
"dark_vs": "meta.diff.header: #569CD6",
|
||||
"light_vs": "meta.diff.header: #000080",
|
||||
"hc_black": "meta.diff.header: #000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "index e4f37c4..557db03 100644",
|
||||
"t": "source.diff meta.diff.index.git",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "---",
|
||||
"t": "source.diff meta.diff.header.from-file punctuation.definition.from-file.diff",
|
||||
"r": {
|
||||
"dark_plus": "meta.diff.header: #569CD6",
|
||||
"light_plus": "meta.diff.header: #000080",
|
||||
"dark_vs": "meta.diff.header: #569CD6",
|
||||
"light_vs": "meta.diff.header: #000080",
|
||||
"hc_black": "meta.diff.header: #000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " a/helloworld.txt",
|
||||
"t": "source.diff meta.diff.header.from-file",
|
||||
"r": {
|
||||
"dark_plus": "meta.diff.header: #569CD6",
|
||||
"light_plus": "meta.diff.header: #000080",
|
||||
"dark_vs": "meta.diff.header: #569CD6",
|
||||
"light_vs": "meta.diff.header: #000080",
|
||||
"hc_black": "meta.diff.header: #000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "+++",
|
||||
"t": "source.diff meta.diff.header.to-file punctuation.definition.to-file.diff",
|
||||
"r": {
|
||||
"dark_plus": "meta.diff.header: #569CD6",
|
||||
"light_plus": "meta.diff.header: #000080",
|
||||
"dark_vs": "meta.diff.header: #569CD6",
|
||||
"light_vs": "meta.diff.header: #000080",
|
||||
"hc_black": "meta.diff.header: #000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " b/helloworld.txt",
|
||||
"t": "source.diff meta.diff.header.to-file",
|
||||
"r": {
|
||||
"dark_plus": "meta.diff.header: #569CD6",
|
||||
"light_plus": "meta.diff.header: #000080",
|
||||
"dark_vs": "meta.diff.header: #569CD6",
|
||||
"light_vs": "meta.diff.header: #000080",
|
||||
"hc_black": "meta.diff.header: #000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "@@",
|
||||
"t": "source.diff meta.diff.range.unified punctuation.definition.range.diff",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "source.diff meta.diff.range.unified",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "-1 +1",
|
||||
"t": "source.diff meta.diff.range.unified meta.toc-list.line-number.diff",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "source.diff meta.diff.range.unified",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "@@",
|
||||
"t": "source.diff meta.diff.range.unified punctuation.definition.range.diff",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "-",
|
||||
"t": "source.diff markup.deleted.diff punctuation.definition.deleted.diff",
|
||||
"r": {
|
||||
"dark_plus": "markup.deleted: #CE9178",
|
||||
"light_plus": "markup.deleted: #A31515",
|
||||
"dark_vs": "markup.deleted: #CE9178",
|
||||
"light_vs": "markup.deleted: #A31515",
|
||||
"hc_black": "markup.deleted: #CE9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "Hello world",
|
||||
"t": "source.diff markup.deleted.diff",
|
||||
"r": {
|
||||
"dark_plus": "markup.deleted: #CE9178",
|
||||
"light_plus": "markup.deleted: #A31515",
|
||||
"dark_vs": "markup.deleted: #CE9178",
|
||||
"light_vs": "markup.deleted: #A31515",
|
||||
"hc_black": "markup.deleted: #CE9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "+",
|
||||
"t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff",
|
||||
"r": {
|
||||
"dark_plus": "markup.inserted: #B5CEA8",
|
||||
"light_plus": "markup.inserted: #098658",
|
||||
"dark_vs": "markup.inserted: #B5CEA8",
|
||||
"light_vs": "markup.inserted: #098658",
|
||||
"hc_black": "markup.inserted: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "Hello World",
|
||||
"t": "source.diff markup.inserted.diff",
|
||||
"r": {
|
||||
"dark_plus": "markup.inserted: #B5CEA8",
|
||||
"light_plus": "markup.inserted: #098658",
|
||||
"dark_vs": "markup.inserted: #B5CEA8",
|
||||
"light_vs": "markup.inserted: #098658",
|
||||
"hc_black": "markup.inserted: #B5CEA8"
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,541 @@
|
||||
[
|
||||
{
|
||||
"c": "pick",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "support.function.git-rebase: #9CDCFE",
|
||||
"light_plus": "support.function.git-rebase: #0451A5",
|
||||
"dark_vs": "support.function.git-rebase: #9CDCFE",
|
||||
"light_vs": "support.function.git-rebase: #0451A5",
|
||||
"hc_black": "support.function.git-rebase: #D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "1fc6c95",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_plus": "constant.sha.git-rebase: #098658",
|
||||
"dark_vs": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_vs": "constant.sha.git-rebase: #098658",
|
||||
"hc_black": "constant.sha.git-rebase: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "Patch A",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "squash",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "support.function.git-rebase: #9CDCFE",
|
||||
"light_plus": "support.function.git-rebase: #0451A5",
|
||||
"dark_vs": "support.function.git-rebase: #9CDCFE",
|
||||
"light_vs": "support.function.git-rebase: #0451A5",
|
||||
"hc_black": "support.function.git-rebase: #D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "fa39187",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_plus": "constant.sha.git-rebase: #098658",
|
||||
"dark_vs": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_vs": "constant.sha.git-rebase: #098658",
|
||||
"hc_black": "constant.sha.git-rebase: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "Something to add to patch A",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "pick",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "support.function.git-rebase: #9CDCFE",
|
||||
"light_plus": "support.function.git-rebase: #0451A5",
|
||||
"dark_vs": "support.function.git-rebase: #9CDCFE",
|
||||
"light_vs": "support.function.git-rebase: #0451A5",
|
||||
"hc_black": "support.function.git-rebase: #D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "7b36971",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_plus": "constant.sha.git-rebase: #098658",
|
||||
"dark_vs": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_vs": "constant.sha.git-rebase: #098658",
|
||||
"hc_black": "constant.sha.git-rebase: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "Something to move before patch B",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "pick",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "support.function.git-rebase: #9CDCFE",
|
||||
"light_plus": "support.function.git-rebase: #0451A5",
|
||||
"dark_vs": "support.function.git-rebase: #9CDCFE",
|
||||
"light_vs": "support.function.git-rebase: #0451A5",
|
||||
"hc_black": "support.function.git-rebase: #D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "6b2481b",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_plus": "constant.sha.git-rebase: #098658",
|
||||
"dark_vs": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_vs": "constant.sha.git-rebase: #098658",
|
||||
"hc_black": "constant.sha.git-rebase: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "Patch B",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "fixup",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "support.function.git-rebase: #9CDCFE",
|
||||
"light_plus": "support.function.git-rebase: #0451A5",
|
||||
"dark_vs": "support.function.git-rebase: #9CDCFE",
|
||||
"light_vs": "support.function.git-rebase: #0451A5",
|
||||
"hc_black": "support.function.git-rebase: #D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "c619268",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_plus": "constant.sha.git-rebase: #098658",
|
||||
"dark_vs": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_vs": "constant.sha.git-rebase: #098658",
|
||||
"hc_black": "constant.sha.git-rebase: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "A fix for Patch B",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "edit",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "support.function.git-rebase: #9CDCFE",
|
||||
"light_plus": "support.function.git-rebase: #0451A5",
|
||||
"dark_vs": "support.function.git-rebase: #9CDCFE",
|
||||
"light_vs": "support.function.git-rebase: #0451A5",
|
||||
"hc_black": "support.function.git-rebase: #D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "dd1475d",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_plus": "constant.sha.git-rebase: #098658",
|
||||
"dark_vs": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_vs": "constant.sha.git-rebase: #098658",
|
||||
"hc_black": "constant.sha.git-rebase: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "Something I want to split",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "reword",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase support.function.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "support.function.git-rebase: #9CDCFE",
|
||||
"light_plus": "support.function.git-rebase: #0451A5",
|
||||
"dark_vs": "support.function.git-rebase: #9CDCFE",
|
||||
"light_vs": "support.function.git-rebase: #0451A5",
|
||||
"hc_black": "support.function.git-rebase: #D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "4ca2acc",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_plus": "constant.sha.git-rebase: #098658",
|
||||
"dark_vs": "constant.sha.git-rebase: #B5CEA8",
|
||||
"light_vs": "constant.sha.git-rebase: #098658",
|
||||
"hc_black": "constant.sha.git-rebase: #B5CEA8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " ",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "i cant' typ goods",
|
||||
"t": "text.git-rebase meta.commit-command.git-rebase meta.commit-message.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "default: #D4D4D4",
|
||||
"light_plus": "default: #000000",
|
||||
"dark_vs": "default: #D4D4D4",
|
||||
"light_vs": "default: #000000",
|
||||
"hc_black": "default: #FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " Commands:",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " p, pick = use commit",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " r, reword = use commit, but edit the commit message",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " e, edit = use commit, but stop for amending",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " s, squash = use commit, but meld into previous commit",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " f, fixup = like \"squash\", but discard this commit's log message",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": "#",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase punctuation.definition.comment.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
},
|
||||
{
|
||||
"c": " x, exec = run command (the rest of the line) using shell",
|
||||
"t": "text.git-rebase comment.line.number-sign.git-rebase",
|
||||
"r": {
|
||||
"dark_plus": "comment: #6A9955",
|
||||
"light_plus": "comment: #008000",
|
||||
"dark_vs": "comment: #6A9955",
|
||||
"light_vs": "comment: #008000",
|
||||
"hc_black": "comment: #7CA668"
|
||||
}
|
||||
}
|
||||
]
|
1
lib/vscode/extensions/git/test/mocha.opts
Normal file
@ -0,0 +1 @@
|
||||
--ui tdd out/test
|
13
lib/vscode/extensions/git/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out",
|
||||
"experimentalDecorators": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
920
lib/vscode/extensions/git/yarn.lock
Normal file
@ -0,0 +1,920 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/byline@4.2.31":
|
||||
version "4.2.31"
|
||||
resolved "https://registry.yarnpkg.com/@types/byline/-/byline-4.2.31.tgz#0e61fcb9c03e047d21c4496554c7116297ab60cd"
|
||||
integrity sha1-DmH8ucA+BH0hxEllVMcRYperYM0=
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/file-type@^5.2.1":
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/file-type/-/file-type-5.2.1.tgz#e7af49e08187b6b7598509c5e416669d25fa3461"
|
||||
integrity sha512-Im0cJaIPJbbpuW91OrjXnqWPZCJK/tcFy2cFX+1qjG1gubgVZPPO9OVsTVAjotN4I1E6FAV0eIqt+rR8Y1c3iA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/mocha@2.2.43":
|
||||
version "2.2.43"
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.43.tgz#03c54589c43ad048cbcbfd63999b55d0424eec27"
|
||||
integrity sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw==
|
||||
|
||||
"@types/node@*":
|
||||
version "8.0.51"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb"
|
||||
integrity sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ==
|
||||
|
||||
"@types/node@^12.12.31":
|
||||
version "12.12.31"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.31.tgz#d6b4f9645fee17f11319b508fb1001797425da51"
|
||||
integrity sha512-T+wnJno8uh27G9c+1T+a1/WYCHzLeDqtsGJkoEdSp2X8RTh3oOCZQcUnjAx90CS8cmmADX51O0FI/tu9s0yssg==
|
||||
|
||||
"@types/which@^1.0.28":
|
||||
version "1.0.28"
|
||||
resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6"
|
||||
integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY=
|
||||
|
||||
agent-base@4, agent-base@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
|
||||
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
|
||||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
ajv@^6.5.5:
|
||||
version "6.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9"
|
||||
integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ansi-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||
|
||||
applicationinsights@1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5"
|
||||
integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg==
|
||||
dependencies:
|
||||
diagnostic-channel "0.2.0"
|
||||
diagnostic-channel-publishers "0.2.1"
|
||||
zone.js "0.7.6"
|
||||
|
||||
asn1@~0.2.3:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
|
||||
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
|
||||
dependencies:
|
||||
safer-buffer "~2.1.0"
|
||||
|
||||
assert-plus@1.0.0, assert-plus@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||
|
||||
aws-sign2@~0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
|
||||
|
||||
aws4@^1.8.0:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
||||
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
bcrypt-pbkdf@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
|
||||
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
|
||||
dependencies:
|
||||
tweetnacl "^0.14.3"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
|
||||
integrity sha1-wHshHHyVLsH479Uad+8NHTmQopI=
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
browser-stdout@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
|
||||
integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8=
|
||||
|
||||
browser-stdout@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
|
||||
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
byline@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
|
||||
integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
charenc@~0.0.1:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
||||
|
||||
combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@2.15.1:
|
||||
version "2.15.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
|
||||
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
|
||||
|
||||
commander@2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
|
||||
integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=
|
||||
dependencies:
|
||||
graceful-readlink ">= 1.0.0"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
core-util-is@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
crypt@~0.0.1:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
||||
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
|
||||
|
||||
dashdash@^1.12.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
||||
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
debug@2.6.8:
|
||||
version "2.6.8"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
|
||||
integrity sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^2.2.0:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
diagnostic-channel-publishers@0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
|
||||
integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM=
|
||||
|
||||
diagnostic-channel@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17"
|
||||
integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=
|
||||
dependencies:
|
||||
semver "^5.3.0"
|
||||
|
||||
diff@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
|
||||
integrity sha1-yc45Okt8vQsFinJck98pkCeGj/k=
|
||||
|
||||
diff@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||
|
||||
ecc-jsbn@~0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
|
||||
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
|
||||
dependencies:
|
||||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
es6-promise@^4.0.3:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
||||
|
||||
es6-promisify@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
|
||||
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
|
||||
dependencies:
|
||||
es6-promise "^4.0.3"
|
||||
|
||||
escape-string-regexp@1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
extend@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||
|
||||
extsprintf@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
|
||||
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
|
||||
|
||||
extsprintf@^1.2.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
||||
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
|
||||
file-type@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74"
|
||||
integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q=
|
||||
|
||||
forever-agent@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
|
||||
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
|
||||
|
||||
form-data@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
getpass@^0.1.1:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
|
||||
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
glob@7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
|
||||
integrity sha1-gFIR3wT6rxxjo2ADBs31reULLsg=
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.2"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
||||
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.1.2:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
"graceful-readlink@>= 1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
||||
|
||||
growl@1.10.5:
|
||||
version "1.10.5"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
|
||||
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
|
||||
|
||||
growl@1.9.2:
|
||||
version "1.9.2"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
|
||||
integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=
|
||||
|
||||
har-schema@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
|
||||
|
||||
har-validator@~5.1.3:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
|
||||
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
|
||||
dependencies:
|
||||
ajv "^6.5.5"
|
||||
har-schema "^2.0.0"
|
||||
|
||||
has-flag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
|
||||
integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
|
||||
|
||||
has-flag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
he@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
||||
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
|
||||
|
||||
http-proxy-agent@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405"
|
||||
integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==
|
||||
dependencies:
|
||||
agent-base "4"
|
||||
debug "3.1.0"
|
||||
|
||||
http-signature@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
|
||||
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
https-proxy-agent@^2.2.1:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
|
||||
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
|
||||
dependencies:
|
||||
agent-base "^4.3.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
iconv-lite-umd@0.6.8:
|
||||
version "0.6.8"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.8.tgz#5ad310ec126b260621471a2d586f7f37b9958ec0"
|
||||
integrity sha512-zvXJ5gSwMC9JD3wDzH8CoZGc1pbiJn12Tqjk8BXYCnYz3hYL5GRjHW8LEykjXhV9WgNGI4rgpgHcbIiBfrRq6A==
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
is-buffer@~1.1.1:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||
|
||||
is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||
|
||||
isstream@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
|
||||
|
||||
jschardet@2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-2.2.1.tgz#03b0264669a90c7a5c436a68c5a7d4e4cb0c9823"
|
||||
integrity sha512-Ks2JNuUJoc7PGaZ7bVFtSEvOcr0rBq6Q1J5/7+zKWLT+g+4zziL63O0jg7y2jxhzIa1LVsHUbPXrbaWmz9iwDw==
|
||||
|
||||
json-schema-traverse@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json-schema@0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
|
||||
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
|
||||
|
||||
json-stringify-safe@~5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
|
||||
|
||||
json3@3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
|
||||
integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
|
||||
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
|
||||
dependencies:
|
||||
assert-plus "1.0.0"
|
||||
extsprintf "1.3.0"
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
lodash._baseassign@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
|
||||
integrity sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=
|
||||
dependencies:
|
||||
lodash._basecopy "^3.0.0"
|
||||
lodash.keys "^3.0.0"
|
||||
|
||||
lodash._basecopy@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
|
||||
integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=
|
||||
|
||||
lodash._basecreate@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821"
|
||||
integrity sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=
|
||||
|
||||
lodash._getnative@^3.0.0:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
|
||||
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
|
||||
|
||||
lodash._isiterateecall@^3.0.0:
|
||||
version "3.0.9"
|
||||
resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
|
||||
integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=
|
||||
|
||||
lodash.create@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7"
|
||||
integrity sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=
|
||||
dependencies:
|
||||
lodash._baseassign "^3.0.0"
|
||||
lodash._basecreate "^3.0.0"
|
||||
lodash._isiterateecall "^3.0.0"
|
||||
|
||||
lodash.isarguments@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
|
||||
integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=
|
||||
|
||||
lodash.isarray@^3.0.0:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
|
||||
integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=
|
||||
|
||||
lodash.keys@^3.0.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
|
||||
integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=
|
||||
dependencies:
|
||||
lodash._getnative "^3.0.0"
|
||||
lodash.isarguments "^3.0.0"
|
||||
lodash.isarray "^3.0.0"
|
||||
|
||||
lodash@^4.16.4:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
|
||||
md5@^2.1.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
|
||||
integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
|
||||
dependencies:
|
||||
charenc "~0.0.1"
|
||||
crypt "~0.0.1"
|
||||
is-buffer "~1.1.1"
|
||||
|
||||
mime-db@1.43.0:
|
||||
version "1.43.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
|
||||
integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
|
||||
|
||||
mime-types@^2.1.12, mime-types@~2.1.19:
|
||||
version "2.1.26"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
|
||||
integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
|
||||
dependencies:
|
||||
mime-db "1.43.0"
|
||||
|
||||
minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
|
||||
mkdirp@0.5.1, mkdirp@~0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
mocha-junit-reporter@^1.23.3:
|
||||
version "1.23.3"
|
||||
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981"
|
||||
integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA==
|
||||
dependencies:
|
||||
debug "^2.2.0"
|
||||
md5 "^2.1.0"
|
||||
mkdirp "~0.5.1"
|
||||
strip-ansi "^4.0.0"
|
||||
xml "^1.0.0"
|
||||
|
||||
mocha-multi-reporters@^1.1.7:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82"
|
||||
integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI=
|
||||
dependencies:
|
||||
debug "^3.1.0"
|
||||
lodash "^4.16.4"
|
||||
|
||||
mocha@^3.2.0:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d"
|
||||
integrity sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==
|
||||
dependencies:
|
||||
browser-stdout "1.3.0"
|
||||
commander "2.9.0"
|
||||
debug "2.6.8"
|
||||
diff "3.2.0"
|
||||
escape-string-regexp "1.0.5"
|
||||
glob "7.1.1"
|
||||
growl "1.9.2"
|
||||
he "1.1.1"
|
||||
json3 "3.3.2"
|
||||
lodash.create "3.1.1"
|
||||
mkdirp "0.5.1"
|
||||
supports-color "3.1.2"
|
||||
|
||||
mocha@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
|
||||
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
|
||||
dependencies:
|
||||
browser-stdout "1.3.1"
|
||||
commander "2.15.1"
|
||||
debug "3.1.0"
|
||||
diff "3.5.0"
|
||||
escape-string-regexp "1.0.5"
|
||||
glob "7.1.2"
|
||||
growl "1.10.5"
|
||||
he "1.1.1"
|
||||
minimatch "3.0.4"
|
||||
mkdirp "0.5.1"
|
||||
supports-color "5.4.0"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
oauth-sign@~0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
performance-now@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
psl@^1.1.28:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
|
||||
integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
|
||||
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||
|
||||
querystringify@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
|
||||
request@^2.88.0:
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||
dependencies:
|
||||
aws-sign2 "~0.7.0"
|
||||
aws4 "^1.8.0"
|
||||
caseless "~0.12.0"
|
||||
combined-stream "~1.0.6"
|
||||
extend "~3.0.2"
|
||||
forever-agent "~0.6.1"
|
||||
form-data "~2.3.2"
|
||||
har-validator "~5.1.3"
|
||||
http-signature "~1.2.0"
|
||||
is-typedarray "~1.0.0"
|
||||
isstream "~0.1.2"
|
||||
json-stringify-safe "~5.0.1"
|
||||
mime-types "~2.1.19"
|
||||
oauth-sign "~0.9.0"
|
||||
performance-now "^2.1.0"
|
||||
qs "~6.5.2"
|
||||
safe-buffer "^5.1.2"
|
||||
tough-cookie "~2.5.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
||||
|
||||
safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
semver@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
|
||||
|
||||
semver@^5.4.1:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||
|
||||
source-map-support@^0.5.0:
|
||||
version "0.5.16"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
|
||||
integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
sshpk@^1.7.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
|
||||
dependencies:
|
||||
asn1 "~0.2.3"
|
||||
assert-plus "^1.0.0"
|
||||
bcrypt-pbkdf "^1.0.0"
|
||||
dashdash "^1.12.0"
|
||||
ecc-jsbn "~0.1.1"
|
||||
getpass "^0.1.1"
|
||||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.0.2"
|
||||
tweetnacl "~0.14.0"
|
||||
|
||||
strip-ansi@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
|
||||
supports-color@3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
|
||||
integrity sha1-cqJiiU2dQIuVbKBf83su2KbiotU=
|
||||
dependencies:
|
||||
has-flag "^1.0.0"
|
||||
|
||||
supports-color@5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
|
||||
integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
||||
dependencies:
|
||||
psl "^1.1.28"
|
||||
punycode "^2.1.1"
|
||||
|
||||
tunnel-agent@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
|
||||
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
version "0.14.5"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url-parse@^1.4.4:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
|
||||
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
verror@1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
|
||||
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
vscode-extension-telemetry@0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b"
|
||||
integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA==
|
||||
dependencies:
|
||||
applicationinsights "1.0.8"
|
||||
|
||||
vscode-nls@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
|
||||
integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
|
||||
|
||||
vscode-test@^0.4.1:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8"
|
||||
integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==
|
||||
dependencies:
|
||||
http-proxy-agent "^2.1.0"
|
||||
https-proxy-agent "^2.2.1"
|
||||
|
||||
vscode-uri@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542"
|
||||
integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw==
|
||||
|
||||
vscode@^1.1.36:
|
||||
version "1.1.36"
|
||||
resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6"
|
||||
integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ==
|
||||
dependencies:
|
||||
glob "^7.1.2"
|
||||
mocha "^5.2.0"
|
||||
request "^2.88.0"
|
||||
semver "^5.4.1"
|
||||
source-map-support "^0.5.0"
|
||||
url-parse "^1.4.4"
|
||||
vscode-test "^0.4.1"
|
||||
|
||||
which@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
|
||||
integrity sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
xml@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
|
||||
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
|
||||
|
||||
zone.js@0.7.6:
|
||||
version "0.7.6"
|
||||
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"
|
||||
integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=
|