Merge vscode 1.67 (#20883)

* Fix initial build breaks from 1.67 merge (#2514)

* Update yarn lock files

* Update build scripts

* Fix tsconfig

* Build breaks

* WIP

* Update yarn lock files

* Misc breaks

* Updates to package.json

* Breaks

* Update yarn

* Fix breaks

* Breaks

* Build breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Missing file

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Fix several runtime breaks (#2515)

* Missing files

* Runtime breaks

* Fix proxy ordering issue

* Remove commented code

* Fix breaks with opening query editor

* Fix post merge break

* Updates related to setup build and other breaks (#2516)

* Fix bundle build issues

* Update distro

* Fix distro merge and update build JS files

* Disable pipeline steps

* Remove stats call

* Update license name

* Make new RPM dependencies a warning

* Fix extension manager version checks

* Update JS file

* Fix a few runtime breaks

* Fixes

* Fix runtime issues

* Fix build breaks

* Update notebook tests (part 1)

* Fix broken tests

* Linting errors

* Fix hygiene

* Disable lint rules

* Bump distro

* Turn off smoke tests

* Disable integration tests

* Remove failing "activate" test

* Remove failed test assertion

* Disable other broken test

* Disable query history tests

* Disable extension unit tests

* Disable failing tasks
This commit is contained in:
Karl Burtram
2022-10-19 19:13:18 -07:00
committed by GitHub
parent 33c6daaea1
commit 8a3d08f0de
3738 changed files with 192313 additions and 107208 deletions

View File

@@ -4,5 +4,4 @@ out/**
tsconfig.json
build/**
extension.webpack.config.js
cgmanifest.json
yarn.lock

View File

@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
var updateGrammar = require('vscode-grammar-updater');
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');

View File

@@ -1,66 +0,0 @@
{
"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
}

View File

@@ -1,11 +0,0 @@
{
"comments": {
"lineComment": "#",
"blockComment": [ "#", " " ]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
]
}

View File

@@ -1,11 +0,0 @@
{
"comments": {
"lineComment": "#",
"blockComment": [ "#", " " ]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
]
}

View File

@@ -1,11 +0,0 @@
{
"comments": {
"lineComment": "#",
"blockComment": [ "#", " " ]
},
"brackets": [
["{", "}"],
["[", "]"],
["(", ")"]
]
}

View File

@@ -1,5 +0,0 @@
{
"comments": {
"lineComment": "#",
}
}

View File

@@ -9,7 +9,14 @@
"vscode": "^1.5.0"
},
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"enableProposedApi": true,
"enabledApiProposals": [
"diffCommand",
"contribViewsWelcome",
"scmActionButton",
"scmSelectedProvider",
"scmValidation",
"timeline"
],
"categories": [
"Other"
],
@@ -17,19 +24,21 @@
"*",
"onFileSystem:git"
],
"extensionDependencies": [
"vscode.git-base"
],
"main": "./out/main",
"icon": "resources/icons/git.png",
"scripts": {
"compile": "gulp compile-extension:git",
"watch": "gulp watch-extension:git",
"update-emoji": "node ./build/update-emoji.js",
"update-grammar": "node ./build/update-grammars.js",
"test": "node ../../node_modules/mocha/bin/mocha"
},
"capabilities": {
"virtualWorkspaces": true,
"untrustedWorkspaces": {
"supported": true
"supported": false
}
},
"contributes": {
@@ -485,6 +494,11 @@
"title": "%command.stashDrop%",
"category": "Git"
},
{
"command": "git.stashDropAll",
"title": "%command.stashDropAll%",
"category": "Git"
},
{
"command": "git.timeline.openDiff",
"title": "%command.timelineOpenDiff%",
@@ -515,6 +529,26 @@
"command": "git.rebaseAbort",
"title": "%command.rebaseAbort%",
"category": "Git"
},
{
"command": "git.closeAllDiffEditors",
"title": "%command.closeAllDiffEditors%",
"category": "Git"
},
{
"command": "git.api.getRepositories",
"title": "%command.api.getRepositories%",
"category": "Git API"
},
{
"command": "git.api.getRepositoryState",
"title": "%command.api.getRepositoryState%",
"category": "Git API"
},
{
"command": "git.api.getRemoteSources",
"title": "%command.api.getRemoteSources%",
"category": "Git API"
}
],
"keybindings": [
@@ -569,15 +603,15 @@
},
{
"command": "git.openFile",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceHasChanges"
},
{
"command": "git.openHEADFile",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceHasChanges"
},
{
"command": "git.openChange",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourcePath in git.changedResources"
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.stage",
@@ -649,7 +683,7 @@
},
{
"command": "git.rename",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file"
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceRepository"
},
{
"command": "git.commit",
@@ -849,7 +883,7 @@
},
{
"command": "git.ignore",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceRepository"
},
{
"command": "git.stashIncludeUntracked",
@@ -879,6 +913,10 @@
"command": "git.stashDrop",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.stashDropAll",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.timeline.openDiff",
"when": "false"
@@ -898,6 +936,22 @@
{
"command": "git.timeline.compareWithSelected",
"when": "false"
},
{
"command": "git.closeAllDiffEditors",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0"
},
{
"command": "git.api.getRepositories",
"when": "false"
},
{
"command": "git.api.getRepositoryState",
"when": "false"
},
{
"command": "git.api.getRemoteSources",
"when": "false"
}
],
"scm/title": [
@@ -931,6 +985,11 @@
"group": "1_header@4",
"when": "scmProvider == git"
},
{
"command": "git.fetch",
"group": "1_header@5",
"when": "scmProvider == git"
},
{
"submenu": "git.commit",
"group": "2_main@1",
@@ -1324,7 +1383,7 @@
{
"command": "git.openChange",
"group": "navigation",
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file && resourcePath in git.changedResources"
"when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file && scmActiveResourceHasChanges"
},
{
"command": "git.stageSelectedRanges",
@@ -1604,6 +1663,10 @@
{
"command": "git.stashDrop",
"group": "stash@7"
},
{
"command": "git.stashDropAll",
"group": "stash@8"
}
],
"git.tags": [
@@ -2064,6 +2127,12 @@
"default": true,
"description": "%config.confirmNoVerifyCommit%"
},
"git.closeDiffOnOperation": {
"type": "boolean",
"scope": "resource",
"default": false,
"description": "%config.closeDiffOnOperation%"
},
"git.openDiffOnClick": {
"type": "boolean",
"scope": "resource",
@@ -2124,6 +2193,11 @@
"default": false,
"description": "%config.useCommitInputAsStashMessage%"
},
"git.useIntegratedAskPass": {
"type": "boolean",
"default": true,
"description": "%config.useIntegratedAskPass%"
},
"git.githubAuthentication": {
"deprecationMessage": "This setting is now deprecated, please use `github.gitAuthentication` instead."
},
@@ -2147,6 +2221,12 @@
"description": "%config.timeline.showAuthor%",
"scope": "window"
},
"git.timeline.showUncommitted": {
"type": "boolean",
"default": false,
"description": "%config.timeline.showUncommitted%",
"scope": "window"
},
"git.showUnpublishedCommitsButton": {
"type": "string",
"enum": [
@@ -2168,6 +2248,45 @@
"scope": "resource",
"default": 10000,
"description": "%config.statusLimit%"
},
"git.experimental.installGuide": {
"type": "string",
"enum": [
"default",
"download"
],
"tags": [
"experimental"
],
"scope": "machine",
"description": "%config.experimental.installGuide%",
"default": "default"
},
"git.repositoryScanIgnoredFolders": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"node_modules"
],
"scope": "resource",
"markdownDescription": "%config.repositoryScanIgnoredFolders%"
},
"git.repositoryScanMaxDepth": {
"type": "number",
"scope": "resource",
"default": 1,
"markdownDescription": "%config.repositoryScanMaxDepth%"
},
"git.commandsToLog": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"scope": "resource",
"markdownDescription": "%config.commandsToLog%"
}
}
},
@@ -2178,7 +2297,8 @@
"defaults": {
"light": "#587c0c",
"dark": "#81b88b",
"highContrast": "#1b5225"
"highContrast": "#1b5225",
"highContrastLight": "#374e06"
}
},
{
@@ -2187,7 +2307,8 @@
"defaults": {
"light": "#895503",
"dark": "#E2C08D",
"highContrast": "#E2C08D"
"highContrast": "#E2C08D",
"highContrastLight": "#895503"
}
},
{
@@ -2196,7 +2317,8 @@
"defaults": {
"light": "#ad0707",
"dark": "#c74e39",
"highContrast": "#c74e39"
"highContrast": "#c74e39",
"highContrastLight": "#ad0707"
}
},
{
@@ -2205,7 +2327,8 @@
"defaults": {
"light": "#007100",
"dark": "#73C991",
"highContrast": "#73C991"
"highContrast": "#73C991",
"highContrastLight": "#007100"
}
},
{
@@ -2214,7 +2337,8 @@
"defaults": {
"light": "#007100",
"dark": "#73C991",
"highContrast": "#73C991"
"highContrast": "#73C991",
"highContrastLight": "#007100"
}
},
{
@@ -2223,7 +2347,8 @@
"defaults": {
"light": "#8E8E90",
"dark": "#8C8C8C",
"highContrast": "#A7A8A9"
"highContrast": "#A7A8A9",
"highContrastLight": "#8e8e90"
}
},
{
@@ -2232,7 +2357,8 @@
"defaults": {
"light": "#895503",
"dark": "#E2C08D",
"highContrast": "#E2C08D"
"highContrast": "#E2C08D",
"highContrastLight": "#895503"
}
},
{
@@ -2241,7 +2367,8 @@
"defaults": {
"light": "#ad0707",
"dark": "#c74e39",
"highContrast": "#c74e39"
"highContrast": "#c74e39",
"highContrastLight": "#ad0707"
}
},
{
@@ -2250,7 +2377,8 @@
"defaults": {
"light": "#ad0707",
"dark": "#e4676b",
"highContrast": "#c74e39"
"highContrast": "#c74e39",
"highContrastLight": "#ad0707"
}
},
{
@@ -2259,82 +2387,11 @@
"defaults": {
"light": "#1258a7",
"dark": "#8db9e2",
"highContrast": "#8db9e2"
"highContrast": "#8db9e2",
"highContrastLight": "#1258a7"
}
}
],
"languages": [
{
"id": "git-commit",
"aliases": [
"Git Commit Message",
"git-commit"
],
"filenames": [
"COMMIT_EDITMSG",
"MERGE_MSG"
],
"configuration": "./languages/git-commit.language-configuration.json"
},
{
"id": "git-rebase",
"aliases": [
"Git Rebase Message",
"git-rebase"
],
"filenames": [
"git-rebase-todo"
],
"configuration": "./languages/git-rebase.language-configuration.json"
},
{
"id": "diff",
"aliases": [
"Diff",
"diff"
],
"extensions": [
".diff",
".patch",
".rej"
],
"configuration": "./languages/diff.language-configuration.json"
},
{
"id": "ignore",
"aliases": [
"Ignore",
"ignore"
],
"extensions": [
".gitignore_global",
".gitignore"
],
"configuration": "./languages/ignore.language-configuration.json"
}
],
"grammars": [
{
"language": "git-commit",
"scopeName": "text.git-commit",
"path": "./syntaxes/git-commit.tmLanguage.json"
},
{
"language": "git-rebase",
"scopeName": "text.git-rebase",
"path": "./syntaxes/git-rebase.tmLanguage.json"
},
{
"language": "diff",
"scopeName": "source.diff",
"path": "./syntaxes/diff.tmLanguage.json"
},
{
"language": "ignore",
"scopeName": "source.ignore",
"path": "./syntaxes/ignore.tmLanguage.json"
}
],
"configurationDefaults": {
"[git-commit]": {
"editor.rulers": [
@@ -2352,10 +2409,30 @@
"contents": "%view.workbench.scm.disabled%",
"when": "!config.git.enabled"
},
{
"view": "scm",
"contents": "%view.workbench.scm.missing.guide%",
"when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download"
},
{
"view": "scm",
"contents": "%view.workbench.scm.missing.guide.mac%",
"when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isMac"
},
{
"view": "scm",
"contents": "%view.workbench.scm.missing.guide.windows%",
"when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isWindows"
},
{
"view": "scm",
"contents": "%view.workbench.scm.missing.guide.linux%",
"when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isLinux"
},
{
"view": "scm",
"contents": "%view.workbench.scm.missing%",
"when": "config.git.enabled && git.missing"
"when": "config.git.enabled && git.missing && config.git.experimental.installGuide == default"
},
{
"view": "scm",
@@ -2402,19 +2479,19 @@
]
},
"dependencies": {
"@vscode/extension-telemetry": "0.4.10",
"@vscode/iconv-lite-umd": "0.7.0",
"byline": "^5.0.0",
"file-type": "16.5.4",
"iconv-lite-umd": "0.6.8",
"jschardet": "3.0.0",
"vscode-extension-telemetry": "0.4.2",
"vscode-nls": "^4.0.0",
"vscode-uri": "^2.0.0",
"which": "^1.3.0"
},
"devDependencies": {
"@types/byline": "4.2.31",
"@types/mocha": "^8.2.0",
"@types/node": "14.x",
"@types/mocha": "^9.1.1",
"@types/node": "16.x",
"@types/which": "^1.0.28"
},
"repository": {

View File

@@ -29,6 +29,7 @@
"command.cleanAll": "Discard All Changes",
"command.cleanAllTracked": "Discard All Tracked Changes",
"command.cleanAllUntracked": "Discard All Untracked Changes",
"command.closeAllDiffEditors": "Close All Diff Editors",
"command.commit": "Commit",
"command.commitStaged": "Commit Staged",
"command.commitEmpty": "Commit Empty",
@@ -78,7 +79,7 @@
"command.publish": "Publish Branch...",
"command.showOutput": "Show Git Output",
"command.ignore": "Add to .gitignore",
"command.revealInExplorer": "Reveal in Side Bar",
"command.revealInExplorer": "Reveal in Explorer View",
"command.rebaseAbort": "Abort Rebase",
"command.stashIncludeUntracked": "Stash (Include Untracked)",
"command.stash": "Stash",
@@ -87,11 +88,15 @@
"command.stashApply": "Apply Stash...",
"command.stashApplyLatest": "Apply Latest Stash",
"command.stashDrop": "Drop Stash...",
"command.stashDropAll": "Drop All Stashes...",
"command.timelineOpenDiff": "Open Changes",
"command.timelineCopyCommitId": "Copy Commit ID",
"command.timelineCopyCommitMessage": "Copy Commit Message",
"command.timelineSelectForCompare": "Select for Compare",
"command.timelineCompareWithSelected": "Compare with Selected",
"command.api.getRepositories": "Get Repositories",
"command.api.getRepositoryState": "Get Repository State",
"command.api.getRemoteSources": "Get Remote Sources",
"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.",
@@ -158,6 +163,14 @@
"config.ignoreSubmodules": "Ignore modifications to submodules in the file tree.",
"config.ignoredRepositories": "List of git repositories to ignore.",
"config.scanRepositories": "List of paths to search for git repositories in.",
"config.commandsToLog": {
"message": "List of git commands (ex: commit, push) that would have their `stdout` logged to the [git output](command:git.showOutput). If the git command has a client-side hook configured, the client-side hook's `stdout` will also be logged to the [git output](command:git.showOutput).",
"comment": [
"{Locked='](command:git.showOutput'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"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.",
@@ -170,6 +183,7 @@
"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 committing without verification.",
"config.closeDiffOnOperation": "Controls whether the diff editor should be automatically closed when changes are stashed, committed, discarded, staged, or unstaged.",
"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.",
@@ -181,6 +195,7 @@
"config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.",
"config.terminalAuthentication": "Controls whether to enable Azure Data Studio 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.showUncommitted": "Controls whether to show uncommitted changes 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",
@@ -190,6 +205,10 @@
"config.showUnpublishedCommitsButton.whenEmpty": "Only shows the action button if there are no other changes and there are unpublished commits.",
"config.showUnpublishedCommitsButton.never": "Never shows the action button.",
"config.statusLimit": "Controls how to limit the number of changes that can be parsed from Git status command. Can be set to 0 for no limit.",
"config.experimental.installGuide": "Experimental improvements for the git setup flow.",
"config.repositoryScanIgnoredFolders": "List of folders that are ignored while scanning for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`.",
"config.repositoryScanMaxDepth": "Controls the depth used when scanning workspace folders for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`. Can be set to `-1` for no limit.",
"config.useIntegratedAskPass": "Controls whether GIT_ASKPASS should be overwritten to use the integrated version.",
"submenu.explorer": "Git",
"submenu.commit": "Commit",
"submenu.commit.amend": "Amend",
@@ -210,12 +229,86 @@
"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 Azure Data Studio 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 Azure Data Studio [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 Azure Data Studio [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 Azure Data Studio [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 Azure Data Studio [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 Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"view.workbench.cloneRepository": "You can clone a repository locally.\n[Clone Repository](command:git.clone 'Clone a repository once the git extension has activated')",
"view.workbench.scm.missing": {
"message": "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 Azure Data Studio 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.",
"comment": [
"{Locked='](command:git.showOutput'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.missing.guide.windows": {
"message": "[Download Git for Windows](https://git-scm.com/download/win)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).",
"comment": [
"{Locked='](command:workbench.action.reloadWindow'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.missing.guide.mac": {
"message": "[Download Git for macOS](https://git-scm.com/download/mac)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).",
"comment": [
"{Locked='](command:workbench.action.reloadWindow'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.missing.guide.linux": {
"message": "Source control depends on Git being installed.\n[Download Git for Linux](https://git-scm.com/download/linux)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).",
"comment": [
"{Locked='](command:workbench.action.reloadWindow'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.missing.guide": "Install Git, a popular source control system, to track code changes and collaborate with others. Learn more in our [Git guides](https://aka.ms/vscode-scm).",
"view.workbench.scm.disabled": {
"message": "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).",
"comment": [
"{Locked='](command:workbench.action.openSettings?%5B%22git.enabled%22%5D'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.empty": {
"message": "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 Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"comment": [
"{Locked='](command:vscode.openFolder'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.folder": {
"message": "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 Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"comment": [
"{Locked='](command:git.init?%5Btrue%5D'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.workspace": {
"message": "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 Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"comment": [
"{Locked='](command:git.init'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.scm.emptyWorkspace": {
"message": "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 Azure Data Studio [read our docs](https://aka.ms/vscode-scm).",
"comment": [
"{Locked='](command:workbench.action.addRootFolder'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.cloneRepository": {
"message": "You can clone a repository locally.\n[Clone Repository](command:git.clone 'Clone a repository once the git extension has activated')",
"comment": [
"{Locked='](command:git.clone'}",
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for Azure Data Studio",
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
]
},
"view.workbench.learnMore": "To learn more about how to use git and source control in Azure Data Studio [read our docs](https://aka.ms/vscode-scm)."
}

View File

@@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace } from 'vscode';
import * as nls from 'vscode-nls';
import { Repository, Operation } from './repository';
import { dispose } from './util';
import { Branch } from './api/git';
const localize = nls.loadMessageBundle();
interface ActionButtonState {
readonly HEAD: Branch | undefined;
readonly isSyncRunning: boolean;
readonly repositoryHasNoChanges: boolean;
}
export class ActionButtonCommand {
private _onDidChange = new EventEmitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
private _state: ActionButtonState;
private get state() { return this._state; }
private set state(state: ActionButtonState) {
if (JSON.stringify(this._state) !== JSON.stringify(state)) {
this._state = state;
this._onDidChange.fire();
}
}
private disposables: Disposable[] = [];
constructor(readonly repository: Repository) {
this._state = { HEAD: undefined, isSyncRunning: false, repositoryHasNoChanges: false };
repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables);
repository.onDidChangeOperations(this.onDidChangeOperations, this, this.disposables);
}
get button(): SourceControlActionButton | undefined {
if (!this.state.HEAD || !this.state.HEAD.name || !this.state.HEAD.commit) { return undefined; }
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<string>('showUnpublishedCommitsButton', 'whenEmpty');
const postCommitCommand = config.get<string>('postCommitCommand');
const noPostCommitCommand = postCommitCommand !== 'sync' && postCommitCommand !== 'push';
let actionButton: SourceControlActionButton | undefined;
if (showActionButton === 'always' || (showActionButton === 'whenEmpty' && this.state.repositoryHasNoChanges && noPostCommitCommand)) {
if (this.state.HEAD.upstream) {
if (this.state.HEAD.ahead) {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const rebaseWhenSync = config.get<string>('rebaseWhenSync');
const ahead = `${this.state.HEAD.ahead}$(arrow-up)`;
const behind = this.state.HEAD.behind ? `${this.state.HEAD.behind}$(arrow-down) ` : '';
const icon = this.state.isSyncRunning ? '$(sync~spin)' : '$(sync)';
actionButton = {
command: {
command: this.state.isSyncRunning ? '' : rebaseWhenSync ? 'git.syncRebase' : 'git.sync',
title: localize('scm button sync title', "{0} {1}{2}", icon, behind, ahead),
tooltip: this.state.isSyncRunning ?
localize('syncing changes', "Synchronizing Changes...")
: this.repository.syncTooltip,
arguments: [this.repository.sourceControl],
},
description: localize('scm button sync description', "{0} Sync Changes {1}{2}", icon, behind, ahead)
};
}
} else {
actionButton = {
command: {
command: this.state.isSyncRunning ? '' : 'git.publish',
title: localize('scm button publish title', "$(cloud-upload) Publish Branch"),
tooltip: this.state.isSyncRunning ?
localize('scm button publish branch running', "Publishing Branch...") :
localize('scm button publish branch', "Publish Branch"),
arguments: [this.repository.sourceControl],
}
};
}
}
return actionButton;
}
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,
HEAD: this.repository.HEAD,
repositoryHasNoChanges:
this.repository.indexGroup.resourceStates.length === 0 &&
this.repository.mergeGroup.resourceStates.length === 0 &&
this.repository.untrackedGroup.resourceStates.length === 0 &&
this.repository.workingTreeGroup.resourceStates.length === 0
};
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -5,12 +5,13 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, RemoteSourceProvider, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, ICloneOptions } from './git'; // {{SQL CARBON EDIT}} add ICloneOptions
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode'; // {{SQL CARBON EDIT}} add CancellationToken
import { mapEvent } from '../util';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, ICloneOptions } from './git'; // {{SQL CARBON EDIT}} add ICloneOptions
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode';
import { combinedDisposable, mapEvent } from '../util';
import { toGitUri } from '../uri';
import { pickRemoteSource, PickRemoteSourceOptions } from '../remoteSource';
import { GitExtensionImpl } from './extension';
import { GitBaseApi } from '../git-base';
import { PickRemoteSourceOptions } from './git-base';
class ApiInputBox implements InputBox {
set value(value: string) { this._inputBox.value = value; }
@@ -67,7 +68,7 @@ export class ApiRepository implements Repository {
return this._repository.apply(patch, reverse);
}
getConfigs(): Promise<{ key: string; value: string; }[]> {
getConfigs(): Promise<{ key: string; value: string }[]> {
return this._repository.getConfigs();
}
@@ -83,11 +84,11 @@ export class ApiRepository implements Repository {
return this._repository.getGlobalConfig(key);
}
getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number; }> {
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 }> {
detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> {
return this._repository.detectObjectType(object);
}
@@ -103,6 +104,14 @@ export class ApiRepository implements Repository {
return this._repository.getCommit(ref);
}
add(paths: string[]) {
return this._repository.add(paths.map(p => Uri.file(p)));
}
revert(paths: string[]) {
return this._repository.revert(paths.map(p => Uri.file(p)));
}
clean(paths: string[]) {
return this._repository.clean(paths.map(p => Uri.file(p)));
}
@@ -173,6 +182,14 @@ export class ApiRepository implements Repository {
return this._repository.getMergeBase(ref1, ref2);
}
tag(name: string, upstream: string): Promise<void> {
return this._repository.tag(name, upstream);
}
deleteTag(name: string): Promise<void> {
return this._repository.deleteTag(name);
}
status(): Promise<void> {
return this._repository.status();
}
@@ -288,7 +305,18 @@ export class ApiImpl implements API {
}
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
return this._model.registerRemoteSourceProvider(provider);
const disposables: Disposable[] = [];
if (provider.publishRepository) {
disposables.push(this._model.registerRemoteSourcePublisher(provider as RemoteSourcePublisher));
}
disposables.push(GitBaseApi.getAPI().registerRemoteSourceProvider(provider));
return combinedDisposable(disposables);
}
registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable {
return this._model.registerRemoteSourcePublisher(publisher);
}
registerCredentialsProvider(provider: CredentialsProvider): Disposable {
@@ -375,11 +403,7 @@ export function registerAPICommands(extension: GitExtensionImpl): Disposable {
}));
disposables.push(commands.registerCommand('git.api.getRemoteSources', (opts?: PickRemoteSourceOptions) => {
if (!extension.model) {
return;
}
return pickRemoteSource(extension.model, opts as any);
return commands.executeCommand('git-base.api.getRemoteSources', opts);
}));
return Disposable.from(...disposables);

60
extensions/git/src/api/git-base.d.ts vendored Normal file
View File

@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, Event, ProviderResult, Uri } from 'vscode';
export { ProviderResult } from 'vscode';
export interface API {
pickRemoteSource(options: PickRemoteSourceOptions): Promise<string | PickRemoteSourceResult | undefined>;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
}
export interface GitBaseExtension {
readonly enabled: boolean;
readonly onDidChangeEnablement: Event<boolean>;
/**
* Returns a specific API version.
*
* Throws error if git-base extension is disabled. You can listed to the
* [GitBaseExtension.onDidChangeEnablement](#GitBaseExtension.onDidChangeEnablement)
* event to know when the extension becomes enabled/disabled.
*
* @param version Version number.
* @returns API instance
*/
getAPI(version: 1): API;
}
export interface PickRemoteSourceOptions {
readonly providerLabel?: (provider: RemoteSourceProvider) => string;
readonly urlLabel?: string;
readonly providerName?: string;
readonly branch?: boolean; // then result is PickRemoteSourceResult
}
export interface PickRemoteSourceResult {
readonly url: string;
readonly branch?: string;
}
export interface RemoteSource {
readonly name: string;
readonly description?: string;
readonly url: string | string[];
}
export interface RemoteSourceProvider {
readonly name: string;
/**
* Codicon name
*/
readonly icon?: string;
readonly supportsQuery?: boolean;
getBranches?(url: string): ProviderResult<string[]>;
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
}

View File

@@ -172,6 +172,8 @@ export interface Repository {
show(ref: string, path: string): Promise<string>;
getCommit(ref: string): Promise<Commit>;
add(paths: string[]): Promise<void>;
revert(paths: string[]): Promise<void>;
clean(paths: string[]): Promise<void>;
apply(patch: string, reverse?: boolean): Promise<void>;
@@ -198,6 +200,9 @@ export interface Repository {
getMergeBase(ref1: string, ref2: string): Promise<string>;
tag(name: string, upstream: string): Promise<void>;
deleteTag(name: string): Promise<void>;
status(): Promise<void>;
checkout(treeish: string): Promise<void>;
@@ -231,6 +236,12 @@ export interface RemoteSourceProvider {
publishRepository?(repository: Repository): Promise<void>;
}
export interface RemoteSourcePublisher {
readonly name: string;
readonly icon?: string; // codicon name
publishRepository(repository: Repository): Promise<void>;
}
export interface Credentials {
readonly username: string;
readonly password: string;
@@ -273,6 +284,7 @@ export interface API {
init(root: Uri): Promise<Repository | null>;
openRepository(root: Uri): Promise<Repository | null>
registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
registerCredentialsProvider(provider: CredentialsProvider): Disposable;
registerPushErrorHandler(handler: PushErrorHandler): Disposable;

View File

@@ -1,5 +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" $*
ELECTRON_RUN_AS_NODE="1" VSCODE_GIT_ASKPASS_PIPE="$VSCODE_GIT_ASKPASS_PIPE" "$VSCODE_GIT_ASKPASS_NODE" "$VSCODE_GIT_ASKPASS_MAIN" $VSCODE_GIT_ASKPASS_EXTRA_ARGS $*
cat $VSCODE_GIT_ASKPASS_PIPE
rm $VSCODE_GIT_ASKPASS_PIPE

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { window, InputBoxOptions, Uri, OutputChannel, Disposable, workspace } from 'vscode';
import { IDisposable, EmptyDisposable, toDisposable } from './util';
import { IDisposable, EmptyDisposable, toDisposable, logTimestamp } from './util';
import * as path from 'path';
import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer';
import { CredentialsProvider, Credentials } from './api/git';
@@ -19,7 +19,7 @@ export class Askpass implements IIPCHandler {
try {
return new Askpass(await createIPCServer(context));
} catch (err) {
outputChannel.appendLine(`[error] Failed to create git askpass IPC: ${err}`);
outputChannel.appendLine(`${logTimestamp()} [error] Failed to create git askpass IPC: ${err}`);
return new Askpass();
}
}
@@ -30,7 +30,7 @@ export class Askpass implements IIPCHandler {
}
}
async handle({ request, host }: { request: string, host: string }): Promise<string> {
async handle({ request, host }: { request: string; host: string }): Promise<string> {
const config = workspace.getConfiguration('git', null);
const enabled = config.get<boolean>('enabled');
@@ -72,19 +72,26 @@ export class Askpass implements IIPCHandler {
return await window.showInputBox(options) || '';
}
getEnv(): { [key: string]: string; } {
getEnv(): { [key: string]: string } {
if (!this.ipc) {
return {
GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh')
};
}
return {
let env: { [key: string]: string } = {
...this.ipc.getEnv(),
GIT_ASKPASS: path.join(__dirname, 'askpass.sh'),
VSCODE_GIT_ASKPASS_NODE: process.execPath,
VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '',
VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js')
};
const config = workspace.getConfiguration('git');
if (config.get<boolean>('useIntegratedAskPass')) {
env.GIT_ASKPASS = path.join(__dirname, 'askpass.sh');
}
return env;
}
registerCredentialsProvider(provider: CredentialsProvider): Disposable {

View File

@@ -6,15 +6,15 @@
import * as os from 'os';
import * as path from 'path';
import { Command, commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import TelemetryReporter from '@vscode/extension-telemetry';
import * as nls from 'vscode-nls';
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider } from './api/git';
import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher } from './api/git';
import { Git, Stash } from './git';
import { Model } from './model';
import { Repository, Resource, ResourceGroupType } from './repository';
import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging';
import { fromGitUri, toGitUri, isGitUri } from './uri';
import { grep, isDescendant, pathEquals } from './util';
import { grep, isDescendant, logTimestamp, pathEquals, relativePath } from './util';
import { Log, LogLevel } from './log';
import { GitTimelineItem } from './timelineProvider';
import { ApiRepository } from './api/api1';
@@ -186,7 +186,7 @@ function command(commandId: string, options: ScmCommandOptions = {}): Function {
// 'image/bmp'
// ];
async function categorizeResourceByResolution(resources: Resource[]): Promise<{ merge: Resource[], resolved: Resource[], unresolved: Resource[], deletionConflicts: Resource[] }> {
async function categorizeResourceByResolution(resources: Resource[]): Promise<{ merge: Resource[]; resolved: Resource[]; unresolved: Resource[]; deletionConflicts: Resource[] }> {
const selection = resources.filter(s => s instanceof Resource) as Resource[];
const merge = selection.filter(s => s.resourceGroupType === ResourceGroupType.Merge);
const isBothAddedOrModified = (s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED;
@@ -282,7 +282,7 @@ interface PushOptions {
remote?: string;
refspec?: string;
setUpstream?: boolean;
}
};
}
class CommandErrorOutputTextDocumentContentProvider implements TextDocumentContentProvider {
@@ -353,7 +353,7 @@ export class CommandCenter {
}
Log.logLevel = choice.logLevel;
this.outputChannel.appendLine(localize('changed', "Log level changed to: {0}", LogLevel[Log.logLevel]));
this.outputChannel.appendLine(localize('changed', "{0} Log level changed to: {1}", logTimestamp(), LogLevel[Log.logLevel]));
}
@command('git.refresh', { repository: true })
@@ -392,7 +392,7 @@ export class CommandCenter {
async cloneRepository(url?: string, parentPath?: string, options: { recursive?: boolean } = {}): Promise<void> {
if (!url || typeof url !== 'string') {
url = await pickRemoteSource(this.model, {
url = await pickRemoteSource({
providerLabel: provider => localize('clonefrom', "Clone from {0}", provider.name),
urlLabel: localize('repourl', "Clone from URL")
});
@@ -544,7 +544,7 @@ export class CommandCenter {
} else {
const placeHolder = localize('init', "Pick workspace folder to initialize git repo in");
const pick = { label: localize('choose', "Choose Folder...") };
const items: { label: string, folder?: WorkspaceFolder }[] = [
const items: { label: string; folder?: WorkspaceFolder }[] = [
...workspace.workspaceFolders.map(folder => ({ label: folder.name, description: folder.uri.fsPath, folder })),
pick
];
@@ -686,6 +686,10 @@ export class CommandCenter {
}
const activeTextEditor = window.activeTextEditor;
// Must extract these now because opening a new document will change the activeTextEditor reference
const previousVisibleRange = activeTextEditor?.visibleRanges[0];
const previousURI = activeTextEditor?.document.uri;
const previousSelection = activeTextEditor?.selection;
for (const uri of uris) {
const opts: TextDocumentShowOptions = {
@@ -702,18 +706,21 @@ export class CommandCenter {
const document = window.activeTextEditor?.document;
// If the document doesn't match what we opened then don't attempt to select the range
if (document?.uri.toString() !== uri.toString()) {
// Additioanlly if there was no previous document we don't have information to select a range
if (document?.uri.toString() !== uri.toString() || !activeTextEditor || !previousURI || !previousSelection) {
continue;
}
// Check if active text editor has same path as other editor. we cannot compare via
// URI.toString() here because the schemas can be different. Instead we just go by path.
if (activeTextEditor && activeTextEditor.document.uri.path === uri.path && document) {
if (previousURI.path === uri.path && document) {
// preserve not only selection but also visible range
opts.selection = activeTextEditor.selection;
const previousVisibleRanges = activeTextEditor.visibleRanges;
opts.selection = previousSelection;
const editor = await window.showTextDocument(document, opts);
editor.revealRange(previousVisibleRanges[0]);
// This should always be defined but just in case
if (previousVisibleRange) {
editor.revealRange(previousVisibleRange);
}
}
}
}
@@ -796,7 +803,7 @@ export class CommandCenter {
return;
}
const from = path.relative(repository.root, fromUri.fsPath);
const from = relativePath(repository.root, fromUri.fsPath);
let to = await window.showInputBox({
value: from,
valueSelection: [from.length - path.basename(from).length, from.length]
@@ -813,14 +820,14 @@ export class CommandCenter {
@command('git.stage')
async stage(...resourceStates: SourceControlResourceState[]): Promise<void> {
this.outputChannel.appendLine(`git.stage ${resourceStates.length}`);
this.outputChannel.appendLine(`${logTimestamp()} git.stage ${resourceStates.length}`);
resourceStates = resourceStates.filter(s => !!s);
if (resourceStates.length === 0 || (resourceStates[0] && !(resourceStates[0].resourceUri instanceof Uri))) {
const resource = this.getSCMResource();
this.outputChannel.appendLine(`git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null}`);
this.outputChannel.appendLine(`${logTimestamp()} git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null}`);
if (!resource) {
return;
@@ -863,7 +870,7 @@ export class CommandCenter {
const untracked = selection.filter(s => s.resourceGroupType === ResourceGroupType.Untracked);
const scmResources = [...workingTree, ...untracked, ...resolved, ...unresolved];
this.outputChannel.appendLine(`git.stage.scmResources ${scmResources.length}`);
this.outputChannel.appendLine(`${logTimestamp()} git.stage.scmResources ${scmResources.length}`);
if (!scmResources.length) {
return;
}
@@ -1676,7 +1683,7 @@ export class CommandCenter {
return this._checkout(repository, { detached: true, treeish });
}
private async _checkout(repository: Repository, opts?: { detached?: boolean, treeish?: string }): Promise<boolean> {
private async _checkout(repository: Repository, opts?: { detached?: boolean; treeish?: string }): Promise<boolean> {
if (typeof opts?.treeish === 'string') {
await repository.checkout(opts?.treeish, opts);
return true;
@@ -2128,7 +2135,7 @@ export class CommandCenter {
}
const branchName = repository.HEAD.name;
const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName);
const message = localize('confirm publish branch', "The branch '{0}' has no remote branch. Would you like to publish this branch?", branchName);
const yes = localize('ok', "OK");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
@@ -2215,7 +2222,7 @@ export class CommandCenter {
@command('git.addRemote', { repository: true })
async addRemote(repository: Repository): Promise<string | undefined> {
const url = await pickRemoteSource(this.model, {
const url = await pickRemoteSource({
providerLabel: provider => localize('addfrom', "Add remote from {0}", provider.name),
urlLabel: localize('addFrom', "Add remote from URL")
});
@@ -2278,7 +2285,7 @@ export class CommandCenter {
return;
} else if (!HEAD.upstream) {
const branchName = HEAD.name;
const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName);
const message = localize('confirm publish branch', "The branch '{0}' has no remote branch. Would you like to publish this branch?", branchName);
const yes = localize('ok', "OK");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
@@ -2296,7 +2303,7 @@ export class CommandCenter {
const shouldPrompt = !isReadonly && config.get<boolean>('confirmSync') === true;
if (shouldPrompt) {
const message = localize('sync is unpredictable', "This action will push and pull commits to and from '{0}/{1}'.", HEAD.upstream.remote, HEAD.upstream.name);
const message = localize('sync is unpredictable', "This action will pull and push commits from and to '{0}/{1}'.", HEAD.upstream.remote, HEAD.upstream.name);
const yes = localize('ok', "OK");
const neverAgain = localize('never again', "OK, Don't Show Again");
const pick = await window.showWarningMessage(message, { modal: true }, yes, neverAgain);
@@ -2360,19 +2367,19 @@ export class CommandCenter {
const remotes = repository.remotes;
if (remotes.length === 0) {
const providers = this.model.getRemoteProviders().filter(p => !!p.publishRepository);
const publishers = this.model.getRemoteSourcePublishers();
if (providers.length === 0) {
if (publishers.length === 0) {
window.showWarningMessage(localize('no remotes to publish', "Your repository has no remotes configured to publish to."));
return;
}
let provider: RemoteSourceProvider;
let publisher: RemoteSourcePublisher;
if (providers.length === 1) {
provider = providers[0];
if (publishers.length === 1) {
publisher = publishers[0];
} else {
const picks = providers
const picks = publishers
.map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + localize('publish to', "Publish to {0}", provider.name), alwaysShow: true, provider }));
const placeHolder = localize('pick provider', "Pick a provider to publish the branch '{0}' to:", branchName);
const choice = await window.showQuickPick(picks, { placeHolder });
@@ -2381,10 +2388,10 @@ export class CommandCenter {
return;
}
provider = choice.provider;
publisher = choice.provider;
}
await provider.publishRepository!(new ApiRepository(repository));
await publisher.publishRepository(new ApiRepository(repository));
this.model.firePublishEvent(repository, branchName);
return;
@@ -2596,6 +2603,29 @@ export class CommandCenter {
await repository.dropStash(stash.index);
}
@command('git.stashDropAll', { repository: true })
async stashDropAll(repository: Repository): Promise<void> {
const stashes = await repository.getStashes();
if (stashes.length === 0) {
window.showInformationMessage(localize('no stashes', "There are no stashes in the repository."));
return;
}
// request confirmation for the operation
const yes = localize('yes', "Yes");
const question = stashes.length === 1 ?
localize('drop one stash', "Are you sure you want to drop ALL stashes? There is 1 stash that will be subject to pruning, and MAY BE IMPOSSIBLE TO RECOVER.") :
localize('drop all stashes', "Are you sure you want to drop ALL stashes? There are {0} stashes that will be subject to pruning, and MAY BE IMPOSSIBLE TO RECOVER.", stashes.length);
const result = await window.showWarningMessage(question, yes);
if (result !== yes) {
return;
}
await repository.dropStash();
}
private async pickStash(repository: Repository, placeHolder: string): Promise<Stash | undefined> {
const stashes = await repository.getStashes();
@@ -2640,12 +2670,12 @@ export class CommandCenter {
else if (item.previousRef === 'HEAD' && item.ref === '~') {
title = localize('git.title.index', '{0} (Index)', basename);
} else {
title = localize('git.title.diffRefs', '{0} ({1}) {0} ({2})', basename, item.shortPreviousRef, item.shortRef);
title = localize('git.title.diffRefs', '{0} ({1}) {0} ({2})', basename, item.shortPreviousRef, item.shortRef);
}
return {
command: 'vscode.diff',
title: 'Open Comparison',
title: localize('git.timeline.openDiffCommand', "Open Comparison"),
arguments: [toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title, options]
};
}
@@ -2668,7 +2698,7 @@ export class CommandCenter {
env.clipboard.writeText(item.message);
}
private _selectedForCompare: { uri: Uri, item: GitTimelineItem } | undefined;
private _selectedForCompare: { uri: Uri; item: GitTimelineItem } | undefined;
@command('git.timeline.selectForCompare', { repository: false })
async timelineSelectForCompare(item: TimelineItem, uri: Uri | undefined, _source: string) {
@@ -2710,7 +2740,7 @@ export class CommandCenter {
}
const title = localize('git.title.diff', '{0} {1}', leftTitle, rightTitle);
const title = localize('git.title.diff', '{0} {1}', leftTitle, rightTitle);
await commands.executeCommand('vscode.diff', selected.ref === '' ? uri : toGitUri(uri, selected.ref), item.ref === '' ? uri : toGitUri(uri, item.ref), title);
}
@@ -2723,6 +2753,17 @@ export class CommandCenter {
}
}
@command('git.closeAllDiffEditors', { repository: true })
closeDiffEditors(repository: Repository): void {
const resources = [
...repository.indexGroup.resourceStates.map(r => r.resourceUri.fsPath),
...repository.workingTreeGroup.resourceStates.map(r => r.resourceUri.fsPath),
...repository.untrackedGroup.resourceStates.map(r => r.resourceUri.fsPath)
];
repository.closeDiffEditors(resources, resources, true);
}
private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any {
const result = (...args: any[]) => {
let result: Promise<any>;
@@ -2813,7 +2854,7 @@ export class CommandCenter {
type = 'warning';
options.modal = false;
break;
case GitErrorCodes.AuthenticationFailed:
case GitErrorCodes.AuthenticationFailed: {
const regex = /Authentication failed for '(.*)'/i;
const match = regex.exec(err.stderr || String(err));
@@ -2821,12 +2862,13 @@ export class CommandCenter {
? localize('auth failed specific', "Failed to authenticate to git remote:\n\n{0}", match[1])
: localize('auth failed', "Failed to authenticate to git remote.");
break;
}
case GitErrorCodes.NoUserNameConfigured:
case GitErrorCodes.NoUserEmailConfigured:
message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git.");
choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup')));
choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git')));
break;
default:
default: {
const hint = (err.stderr || err.message || String(err))
.replace(/^error: /mi, '')
.replace(/^> husky.*$/mi, '')
@@ -2839,6 +2881,7 @@ export class CommandCenter {
: localize('git error', "Git error");
break;
}
}
if (!message) {
@@ -2870,10 +2913,10 @@ export class CommandCenter {
private getSCMResource(uri?: Uri): Resource | undefined {
uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri);
this.outputChannel.appendLine(`git.getSCMResource.uri ${uri && uri.toString()}`);
this.outputChannel.appendLine(`${logTimestamp()} git.getSCMResource.uri ${uri && uri.toString()}`);
for (const r of this.model.repositories.map(r => r.root)) {
this.outputChannel.appendLine(`repo root ${r}`);
this.outputChannel.appendLine(`${logTimestamp()} repo root ${r}`);
}
if (!uri) {
@@ -2927,7 +2970,7 @@ export class CommandCenter {
}
return result;
}, [] as { repository: Repository, resources: Uri[] }[]);
}, [] as { repository: Repository; resources: Uri[] }[]);
const promises = groups
.map(({ repository, resources }) => fn(repository as Repository, isSingleResource ? resources[0] : resources));

View File

@@ -16,7 +16,7 @@ class GitIgnoreDecorationProvider implements FileDecorationProvider {
private static Decoration: FileDecoration = { color: new ThemeColor('gitDecoration.ignoredResourceForeground') };
readonly onDidChangeFileDecorations: Event<Uri[]>;
private queue = new Map<string, { repository: Repository; queue: Map<string, PromiseSource<FileDecoration | undefined>>; }>();
private queue = new Map<string, { repository: Repository; queue: Map<string, PromiseSource<FileDecoration | undefined>> }>();
private disposables: Disposable[] = [];
constructor(private model: Model) {

View File

@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extensions } from 'vscode';
import { API as GitBaseAPI, GitBaseExtension } from './api/git-base';
export class GitBaseApi {
private static _gitBaseApi: GitBaseAPI | undefined;
static getAPI(): GitBaseAPI {
if (!this._gitBaseApi) {
const gitBaseExtension = extensions.getExtension<GitBaseExtension>('vscode.git-base')!.exports;
const onDidChangeGitBaseExtensionEnablement = (enabled: boolean) => {
this._gitBaseApi = enabled ? gitBaseExtension.getAPI(1) : undefined;
};
gitBaseExtension.onDidChangeEnablement(onDidChangeGitBaseExtensionEnablement);
onDidChangeGitBaseExtensionEnablement(gitBaseExtension.enabled);
if (!this._gitBaseApi) {
throw new Error('vscode.git-base extension is not enabled.');
}
}
return this._gitBaseApi;
}
}

View File

@@ -7,12 +7,13 @@ import { promises as fs, exists, realpath } from 'fs';
import * as path from 'path';
import * as os from 'os';
import * as cp from 'child_process';
import { fileURLToPath } from 'url';
import * as which from 'which';
import { EventEmitter } from 'events';
import * as iconv from 'iconv-lite-umd';
import * as iconv from '@vscode/iconv-lite-umd';
import * as filetype from 'file-type';
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions } from './util';
import { CancellationToken, Uri } from 'vscode';
import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows } from './util';
import { CancellationToken, ConfigurationChangeEvent, Uri, workspace } from 'vscode'; // {{SQL CARBON EDIT}} remove Progress
import { detectEncoding } from './encoding';
import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery, ICloneOptions } from './api/git'; // {{SQL CARBON EDIT}} add ICloneOptions
import * as byline from 'byline';
@@ -20,7 +21,6 @@ import { StringDecoder } from 'string_decoder';
// https://github.com/microsoft/vscode/issues/65693
const MAX_CLI_LENGTH = 30000;
const isWindows = process.platform === 'win32';
export interface IGit {
path: string;
@@ -84,7 +84,7 @@ function findGitDarwin(onValidate: (path: string) => boolean): Promise<IGit> {
return e('git not found');
}
const path = gitPathBuffer.toString().replace(/^\s+|\s+$/g, '');
const path = gitPathBuffer.toString().trim();
function getVersion(path: string) {
if (!onValidate(path)) {
@@ -368,6 +368,7 @@ export class Git {
readonly userAgent: string;
readonly version: string;
private env: any;
private commandsToLog: string[] = [];
private _onOutput = new EventEmitter();
get onOutput(): EventEmitter { return this._onOutput; }
@@ -377,13 +378,25 @@ export class Git {
this.version = options.version;
this.userAgent = options.userAgent;
this.env = options.env || {};
const onConfigurationChanged = (e?: ConfigurationChangeEvent) => {
if (e !== undefined && !e.affectsConfiguration('git.commandsToLog')) {
return;
}
const config = workspace.getConfiguration('git');
this.commandsToLog = config.get<string[]>('commandsToLog', []);
};
workspace.onDidChangeConfiguration(onConfigurationChanged, this);
onConfigurationChanged();
}
compareGitVersionTo(version: string): -1 | 0 | 1 {
return Versions.compare(Versions.fromString(this.version), Versions.fromString(version));
}
open(repository: string, dotGit: string): Repository {
open(repository: string, dotGit: { path: string; commonPath?: string }): Repository {
return new Repository(this, repository, dotGit);
}
@@ -456,7 +469,7 @@ export class Git {
}
async getRepositoryRoot(repositoryPath: string): Promise<string> {
const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel'], { log: false });
const result = await this.exec(repositoryPath, ['rev-parse', '--show-toplevel']);
// Keep trailing spaces which are part of the directory name
const repoPath = path.normalize(result.stdout.trimLeft().replace(/[\r\n]+$/, ''));
@@ -467,6 +480,7 @@ export class Git {
const repoUri = Uri.file(repoPath);
const pathUri = Uri.file(repositoryPath);
if (repoUri.authority.length !== 0 && pathUri.authority.length === 0) {
// eslint-disable-next-line code-no-look-behind-regex
let match = /(?<=^\/?)([a-zA-Z])(?=:\/)/.exec(pathUri.path);
if (match !== null) {
const [, letter] = match;
@@ -495,15 +509,25 @@ export class Git {
return repoPath;
}
async getRepositoryDotGit(repositoryPath: string): Promise<string> {
const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir']);
let dotGitPath = result.stdout.trim();
async getRepositoryDotGit(repositoryPath: string): Promise<{ path: string; commonPath?: string }> {
const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir', '--git-common-dir']);
let [dotGitPath, commonDotGitPath] = result.stdout.split('\n').map(r => r.trim());
if (!path.isAbsolute(dotGitPath)) {
dotGitPath = path.join(repositoryPath, dotGitPath);
}
dotGitPath = path.normalize(dotGitPath);
return path.normalize(dotGitPath);
if (commonDotGitPath) {
if (!path.isAbsolute(commonDotGitPath)) {
commonDotGitPath = path.join(repositoryPath, commonDotGitPath);
}
commonDotGitPath = path.normalize(commonDotGitPath);
return { path: dotGitPath, commonPath: commonDotGitPath !== dotGitPath ? commonDotGitPath : undefined };
}
return { path: dotGitPath };
}
async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
@@ -517,7 +541,16 @@ export class Git {
stream(cwd: string, args: string[], options: SpawnOptions = {}): cp.ChildProcess {
options = assign({ cwd }, options || {});
return this.spawn(args, options);
const child = this.spawn(args, options);
if (options.log !== false) {
const startTime = Date.now();
child.on('exit', (_) => {
this.log(`> git ${args.join(' ')} [${Date.now() - startTime}ms]\n`);
});
}
return child;
}
private async _exec(args: string[], options: SpawnOptions = {}): Promise<IExecutionResult<string>> {
@@ -531,10 +564,22 @@ export class Git {
child.stdin!.end(options.input, 'utf8');
}
const startTime = Date.now();
const bufferResult = await exec(child, options.cancellationToken);
if (options.log !== false && bufferResult.stderr.length > 0) {
this.log(`${bufferResult.stderr}\n`);
if (options.log !== false) {
// command
this.log(`> git ${args.join(' ')} [${Date.now() - startTime}ms]\n`);
// stdout
if (bufferResult.stdout.length > 0 && args.find(a => this.commandsToLog.includes(a))) {
this.log(`${bufferResult.stdout}\n`);
}
// stderr
if (bufferResult.stderr.length > 0) {
this.log(`${bufferResult.stderr}\n`);
}
}
let encoding = options.encoding || 'utf8';
@@ -581,17 +626,27 @@ export class Git {
GIT_PAGER: 'cat'
});
if (options.cwd) {
options.cwd = sanitizePath(options.cwd);
}
if (options.log !== false) {
this.log(`> git ${args.join(' ')}\n`);
const cwd = this.getCwd(options);
if (cwd) {
options.cwd = sanitizePath(cwd);
}
return cp.spawn(this.path, args, options);
}
private getCwd(options: SpawnOptions): string | undefined {
const cwd = options.cwd;
if (typeof cwd === 'undefined' || typeof cwd === 'string') {
return cwd;
}
if (cwd.protocol === 'file:') {
return fileURLToPath(cwd);
}
return undefined;
}
private log(output: string): void {
this._onOutput.emit('log', output);
}
@@ -818,7 +873,7 @@ export class Repository {
constructor(
private _git: Git,
private repositoryRoot: string,
readonly dotGit: string
readonly dotGit: { path: string; commonPath?: string }
) { }
get git(): Git {
@@ -858,7 +913,7 @@ export class Repository {
return result.stdout.trim();
}
async getConfigs(scope: string): Promise<{ key: string; value: string; }[]> {
async getConfigs(scope: string): Promise<{ key: string; value: string }[]> {
const args = ['config'];
if (scope) {
@@ -960,7 +1015,7 @@ export class Repository {
return stdout;
}
async getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }> {
async getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }> {
if (!treeish) { // index
const elements = await this.lsfiles(path);
@@ -998,7 +1053,7 @@ export class Repository {
async getGitRelativePath(ref: string, relativePath: string): Promise<string> {
const relativePathLowercase = relativePath.toLowerCase();
const dirname = path.posix.dirname(relativePath) + '/';
const elements: { file: string; }[] = ref ? await this.lstree(ref, dirname) : await this.lsfiles(dirname);
const elements: { file: string }[] = ref ? await this.lstree(ref, dirname) : await this.lsfiles(dirname);
const element = elements.filter(file => file.file.toLowerCase() === relativePathLowercase)[0];
if (!element) {
@@ -1008,7 +1063,7 @@ export class Repository {
return element.file;
}
async detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> {
async detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> {
const child = await this.stream(['show', '--textconv', object]);
const buffer = await readBytes(child.stdout!, 4100);
@@ -1195,7 +1250,7 @@ export class Repository {
break;
// Rename contains two paths, the second one is what the file is renamed/copied to.
case 'R':
case 'R': {
if (index >= entries.length) {
break;
}
@@ -1214,7 +1269,7 @@ export class Repository {
});
continue;
}
default:
// Unknown status
break entriesLoop;
@@ -1308,7 +1363,7 @@ export class Repository {
await this.exec(['update-index', add, '--cacheinfo', mode, hash, path]);
}
async checkout(treeish: string, paths: string[], opts: { track?: boolean, detached?: boolean } = Object.create(null)): Promise<void> {
async checkout(treeish: string, paths: string[], opts: { track?: boolean; detached?: boolean } = Object.create(null)): Promise<void> {
const args = ['checkout', '-q'];
if (opts.track) {
@@ -1570,7 +1625,7 @@ export class Repository {
await this.exec(args);
}
async fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean, readonly cancellationToken?: CancellationToken } = {}): Promise<void> {
async fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean; readonly cancellationToken?: CancellationToken } = {}): Promise<void> {
const args = ['fetch'];
const spawnOptions: SpawnOptions = {
cancellationToken: options.cancellationToken,
@@ -1793,10 +1848,13 @@ export class Repository {
}
async dropStash(index?: number): Promise<void> {
const args = ['stash', 'drop'];
const args = ['stash'];
if (typeof index === 'number') {
args.push('drop');
args.push(`stash@{${index}}`);
} else {
args.push('clear');
}
try {
@@ -1810,11 +1868,17 @@ export class Repository {
}
}
getStatus(opts?: { limit?: number, ignoreSubmodules?: boolean }): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> {
return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => {
getStatus(opts?: { limit?: number; ignoreSubmodules?: boolean; untrackedChanges?: 'mixed' | 'separate' | 'hidden' }): Promise<{ status: IFileStatus[]; statusLength: number; didHitLimit: boolean }> {
return new Promise<{ status: IFileStatus[]; statusLength: number; didHitLimit: boolean }>((c, e) => {
const parser = new GitStatusParser();
const env = { GIT_OPTIONAL_LOCKS: '0' };
const args = ['status', '-z', '-u'];
const args = ['status', '-z'];
if (opts?.untrackedChanges === 'hidden') {
args.push('-uno');
} else {
args.push('-uall');
}
if (opts?.ignoreSubmodules) {
args.push('--ignore-submodules');
@@ -1835,10 +1899,10 @@ export class Repository {
}));
}
c({ status: parser.status, didHitLimit: false });
c({ status: parser.status, statusLength: parser.status.length, didHitLimit: false });
};
const limit = opts?.limit ?? 5000;
const limit = opts?.limit ?? 10000;
const onStdoutData = (raw: string) => {
parser.update(raw);
@@ -1847,7 +1911,7 @@ export class Repository {
child.stdout!.removeListener('data', onStdoutData);
child.kill();
c({ status: parser.status.slice(0, limit), didHitLimit: true });
c({ status: parser.status.slice(0, limit), statusLength: parser.status.length, didHitLimit: true });
}
};
@@ -1891,7 +1955,7 @@ export class Repository {
.map(([ref]) => ({ name: ref, type: RefType.Head } as Branch));
}
async getRefs(opts?: { sort?: 'alphabetically' | 'committerdate', contains?: string, pattern?: string, count?: number }): Promise<Ref[]> {
async getRefs(opts?: { sort?: 'alphabetically' | 'committerdate'; contains?: string; pattern?: string; count?: number }): Promise<Ref[]> {
const args = ['for-each-ref'];
if (opts?.count) {
@@ -1989,8 +2053,10 @@ export class Repository {
if (this._git.compareGitVersionTo('1.9.0') === -1) {
args.push('--format=%(refname)%00%(upstream:short)%00%(objectname)');
supportsAheadBehind = false;
} else {
} else if (this._git.compareGitVersionTo('2.16.0') === -1) {
args.push('--format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)');
} else {
args.push('--format=%(refname)%00%(upstream:short)%00%(objectname)%00%(upstream:track)%00%(upstream:remotename)%00%(upstream:remoteref)');
}
if (/^refs\/(head|remotes)\//i.test(name)) {
@@ -2001,7 +2067,7 @@ export class Repository {
const result = await this.exec(args);
const branches: Branch[] = result.stdout.trim().split('\n').map<Branch | undefined>(line => {
let [branchName, upstream, ref, status] = line.trim().split('\0');
let [branchName, upstream, ref, status, remoteName, upstreamRef] = line.trim().split('\0');
if (branchName.startsWith('refs/heads/')) {
branchName = branchName.substring(11);
@@ -2018,8 +2084,8 @@ export class Repository {
type: RefType.Head,
name: branchName,
upstream: upstream ? {
name: upstream.substring(index + 1),
remote: upstream.substring(0, index)
name: upstreamRef ? upstreamRef.substring(11) : upstream.substring(index + 1),
remote: remoteName ? remoteName : upstream.substring(0, index)
} : undefined,
commit: ref || undefined,
ahead: Number(ahead) || 0,

View File

@@ -61,7 +61,7 @@ export async function createIPCServer(context?: string): Promise<IIPCServer> {
export interface IIPCServer extends Disposable {
readonly ipcHandlePath: string | undefined;
getEnv(): { [key: string]: string; };
getEnv(): { [key: string]: string };
registerHandler(name: string, handler: IIPCHandler): Disposable;
}
@@ -106,7 +106,7 @@ class IPCServer implements IIPCServer, Disposable {
});
}
getEnv(): { [key: string]: string; } {
getEnv(): { [key: string]: string } {
return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath };
}

View File

@@ -13,8 +13,8 @@ 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 { toDisposable, filterEvent, eventToPromise, logTimestamp } from './util';
import TelemetryReporter from '@vscode/extension-telemetry';
import { GitExtension } from './api/git';
import { GitProtocolHandler } from './protocolHandler';
import { GitExtensionImpl } from './api/extension';
@@ -25,7 +25,7 @@ import { GitTimelineProvider } from './timelineProvider';
import { registerAPICommands } from './api/api1';
import { TerminalEnvironmentManager } from './terminal';
const deactivateTasks: { (): Promise<any>; }[] = [];
const deactivateTasks: { (): Promise<any> }[] = [];
export async function deactivate(): Promise<any> {
for (const task of deactivateTasks) {
@@ -46,7 +46,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
}
const info = await findGit(pathHints, gitPath => {
outputChannel.appendLine(localize('validating', "Validating found git in: {0}", gitPath));
outputChannel.appendLine(localize('validating', "{0} Validating found git in: {1}", logTimestamp(), gitPath));
if (excludes.length === 0) {
return true;
}
@@ -54,7 +54,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
const normalized = path.normalize(gitPath).replace(/[\r\n]+$/, '');
const skip = excludes.some(e => normalized.startsWith(e));
if (skip) {
outputChannel.appendLine(localize('skipped', "Skipped found git in: {0}", gitPath));
outputChannel.appendLine(localize('skipped', "{0} Skipped found git in: {1}", logTimestamp(), gitPath));
}
return !skip;
});
@@ -73,7 +73,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
version: info.version,
env: environment,
});
const model = new Model(git, askpass, context.globalState, outputChannel);
const model = new Model(git, askpass, context.globalState, outputChannel, telemetryReporter);
disposables.push(model);
const onRepository = () => commands.executeCommand('setContext', 'gitOpenRepositoryCount', `${model.repositories.length}`);
@@ -81,7 +81,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
model.onDidCloseRepository(onRepository, null, disposables);
onRepository();
outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path));
outputChannel.appendLine(localize('using git', "{0} Using git {1} from {2}", logTimestamp(), info.version, info.path));
const onOutput = (str: string) => {
const lines = str.split(/\r?\n/mg);
@@ -90,7 +90,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann
lines.pop();
}
outputChannel.appendLine(lines.join('\n'));
outputChannel.appendLine(`${logTimestamp()} ${lines.join('\n')}`);
};
git.onOutput.addListener('log', onOutput);
disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput)));
@@ -152,7 +152,7 @@ async function warnAboutMissingGit(): Promise<void> {
);
if (choice === download) {
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-download-git'));
} else if (choice === neverShowAgain) {
await config.update('ignoreMissingGitWarning', true, true);
}
@@ -166,7 +166,7 @@ export async function _activate(context: ExtensionContext): Promise<GitExtension
commands.registerCommand('git.showOutput', () => outputChannel.show());
disposables.push(outputChannel);
const { name, version, aiKey } = require('../package.json') as { name: string, version: string, aiKey: string };
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());
@@ -193,6 +193,11 @@ export async function _activate(context: ExtensionContext): Promise<GitExtension
// console.warn(err.message); {{SQL CARBON EDIT}} turn-off Git missing prompt
// outputChannel.appendLine(err.message); {{SQL CARBON EDIT}} turn-off Git missing prompt
/* __GDPR__
"git.missing" : {}
*/
telemetryReporter.sendTelemetryEvent('git.missing');
commands.executeCommand('setContext', 'git.missing', true);
// warnAboutMissingGit(); {{SQL CARBON EDIT}} turn-off Git missing prompt

View File

@@ -4,19 +4,21 @@
*--------------------------------------------------------------------------------------------*/
import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, OutputChannel, commands } from 'vscode';
import TelemetryReporter from '@vscode/extension-telemetry';
import { Repository, RepositoryState } from './repository';
import { memoize, sequentialize, debounce } from './decorators';
import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util';
import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise, logTimestamp } 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, PublishEvent } from './api/git';
import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher } from './api/git';
import { Askpass } from './askpass';
import { IRemoteSourceProviderRegistry } from './remoteProvider';
import { IPushErrorHandlerRegistry } from './pushError';
import { ApiRepository } from './api/api1';
import { IRemoteSourcePublisherRegistry } from './remotePublisher';
import { Log, LogLevel } from './log';
const localize = nls.loadMessageBundle();
@@ -48,7 +50,7 @@ interface OpenRepository extends Disposable {
repository: Repository;
}
export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRegistry {
export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerRegistry {
private _onDidOpenRepository = new EventEmitter<Repository>();
readonly onDidOpenRepository: Event<Repository> = this._onDidOpenRepository.event;
@@ -95,19 +97,20 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
return eventToPromise(filterEvent(this.onDidChangeState, s => s === 'initialized')) as Promise<any>;
}
private remoteSourceProviders = new Set<RemoteSourceProvider>();
private remoteSourcePublishers = new Set<RemoteSourcePublisher>();
private _onDidAddRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
readonly onDidAddRemoteSourceProvider = this._onDidAddRemoteSourceProvider.event;
private _onDidAddRemoteSourcePublisher = new EventEmitter<RemoteSourcePublisher>();
readonly onDidAddRemoteSourcePublisher = this._onDidAddRemoteSourcePublisher.event;
private _onDidRemoveRemoteSourceProvider = new EventEmitter<RemoteSourceProvider>();
readonly onDidRemoveRemoteSourceProvider = this._onDidRemoveRemoteSourceProvider.event;
private _onDidRemoveRemoteSourcePublisher = new EventEmitter<RemoteSourcePublisher>();
readonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event;
private showRepoOnHomeDriveRootWarning = true;
private pushErrorHandlers = new Set<PushErrorHandler>();
private disposables: Disposable[] = [];
constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannel: OutputChannel) {
constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannel: OutputChannel, private telemetryReporter: TelemetryReporter) {
workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables);
window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables);
workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables);
@@ -133,25 +136,36 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
}
/**
* Scans the first level of each workspace folder, looking
* for git repositories.
* Scans each workspace folder, looking for git repositories. By
* default it scans one level deep but that can be changed using
* the git.repositoryScanMaxDepth setting.
*/
private async scanWorkspaceFolders(): Promise<void> {
const config = workspace.getConfiguration('git');
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
// Log repository scan settings
if (Log.logLevel <= LogLevel.Trace) {
this.outputChannel.appendLine(`${logTimestamp()} Trace: autoRepositoryDetection="${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 subfolders = new Set(children.filter(child => child !== '.git').map(child => path.join(root, child)));
// Workspace folder children
const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get<number>('repositoryScanMaxDepth', 1);
const repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get<string[]>('repositoryScanIgnoredFolders', []);
const subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders));
// Repository scan folders
const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get<string[]>('scanRepositories') || [];
for (const scanPath of scanPaths) {
if (scanPath !== '.git') {
if (scanPath === '.git') {
continue;
}
@@ -167,6 +181,31 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
}));
}
private async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise<string[]> {
const result: string[] = [];
const foldersToTravers = [{ path: workspaceFolder, depth: 0 }];
while (foldersToTravers.length > 0) {
const currentFolder = foldersToTravers.shift()!;
if (currentFolder.depth < maxDepth || maxDepth === -1) {
const children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true });
const childrenFolders = children
.filter(dirent =>
dirent.isDirectory() && dirent.name !== '.git' &&
!repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f)))
.map(dirent => path.join(currentFolder.path, dirent.name));
result.push(...childrenFolders);
foldersToTravers.push(...childrenFolders.map(folder => {
return { path: folder, depth: currentFolder.depth + 1 };
}));
}
}
return result;
}
private onPossibleGitRepositoryChange(uri: Uri): void {
const config = workspace.getConfiguration('git');
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
@@ -271,7 +310,7 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
// Check if the folder is a bare repo: if it has a file named HEAD && `rev-parse --show -cdup` is empty
try {
fs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK);
const result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup'], { log: false });
const result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']);
if (result.stderr.trim() === '' && result.stdout.trim() === '') {
return;
}
@@ -296,14 +335,32 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
return;
}
// On Window, opening a git repository from the root of the HOMEDRIVE poses a security risk.
// We will only a open git repository from the root of the HOMEDRIVE if the user explicitly
// opens the HOMEDRIVE as a folder. Only show the warning once during repository discovery.
if (process.platform === 'win32' && process.env.HOMEDRIVE && pathEquals(`${process.env.HOMEDRIVE}\\`, repositoryRoot)) {
const isRepoInWorkspaceFolders = (workspace.workspaceFolders ?? []).find(f => pathEquals(f.uri.fsPath, repositoryRoot))!!;
if (!isRepoInWorkspaceFolders) {
if (this.showRepoOnHomeDriveRootWarning) {
window.showWarningMessage(localize('repoOnHomeDriveRootWarning', "Unable to automatically open the git repository at '{0}'. To open that git repository, open it directly as a folder in VS Code.", repositoryRoot));
this.showRepoOnHomeDriveRootWarning = false;
}
return;
}
}
const dotGit = await this.git.getRepositoryDotGit(repositoryRoot);
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel);
const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel, this.telemetryReporter);
this.open(repository);
await repository.status();
repository.status(); // do not await this, we want SCM to know about the repo asap
} catch (ex) {
// noop
this.outputChannel.appendLine(`Opening repository for path='${repoPath}' failed; ex=${ex}`);
if (Log.logLevel <= LogLevel.Trace) {
this.outputChannel.appendLine(`${logTimestamp()} Trace: Opening repository for path='${repoPath}' failed; ex=${ex}`);
}
}
}
@@ -329,7 +386,7 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
}
private open(repository: Repository): void {
this.outputChannel.appendLine(`Open repository: ${repository.root}`);
this.outputChannel.appendLine(`${logTimestamp()} Open repository: ${repository.root}`);
const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed);
const disappearListener = onDidDisappearRepository(() => dispose());
@@ -386,7 +443,7 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
return;
}
this.outputChannel.appendLine(`Close repository: ${repository.root}`);
this.outputChannel.appendLine(`${logTimestamp()} Close repository: ${repository.root}`);
openRepository.dispose();
}
@@ -496,24 +553,24 @@ export class Model implements IRemoteSourceProviderRegistry, IPushErrorHandlerRe
return undefined;
}
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
this.remoteSourceProviders.add(provider);
this._onDidAddRemoteSourceProvider.fire(provider);
registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable {
this.remoteSourcePublishers.add(publisher);
this._onDidAddRemoteSourcePublisher.fire(publisher);
return toDisposable(() => {
this.remoteSourceProviders.delete(provider);
this._onDidRemoveRemoteSourceProvider.fire(provider);
this.remoteSourcePublishers.delete(publisher);
this._onDidRemoveRemoteSourcePublisher.fire(publisher);
});
}
getRemoteSourcePublishers(): RemoteSourcePublisher[] {
return [...this.remoteSourcePublishers.values()];
}
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));

View File

@@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, Event } from 'vscode';
import { RemoteSourceProvider } from './api/git';
import { RemoteSourcePublisher } from './api/git';
export interface IRemoteSourceProviderRegistry {
readonly onDidAddRemoteSourceProvider: Event<RemoteSourceProvider>;
readonly onDidRemoveRemoteSourceProvider: Event<RemoteSourceProvider>;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
getRemoteProviders(): RemoteSourceProvider[];
export interface IRemoteSourcePublisherRegistry {
readonly onDidAddRemoteSourcePublisher: Event<RemoteSourcePublisher>;
readonly onDidRemoveRemoteSourcePublisher: Event<RemoteSourcePublisher>;
getRemoteSourcePublishers(): RemoteSourcePublisher[];
registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable;
}

View File

@@ -3,180 +3,11 @@
* Licensed under the Source EULA. 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';
import { PickRemoteSourceOptions, PickRemoteSourceResult } from './api/git-base';
import { GitBaseApi } from './git-base';
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,
alwaysShow: true
}));
}
} 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;
readonly providerName?: string;
readonly branch?: boolean; // then result is PickRemoteSourceResult
}
export interface PickRemoteSourceResult {
readonly url: string;
readonly branch?: string;
}
export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch?: false | undefined }): Promise<string | undefined>;
export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions & { branch: true }): Promise<PickRemoteSourceResult | undefined>;
export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise<string | PickRemoteSourceResult | undefined> {
const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>();
quickpick.ignoreFocusOut = true;
if (options.providerName) {
const provider = model.getRemoteProviders()
.filter(provider => provider.name === options.providerName)[0];
if (provider) {
return await pickProviderSource(provider, options);
}
}
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) {
return await pickProviderSource(result.provider, options);
}
}
return undefined;
}
async function pickProviderSource(provider: RemoteSourceProvider, options: PickRemoteSourceOptions = {}): Promise<string | PickRemoteSourceResult | undefined> {
const quickpick = new RemoteSourceProviderQuickPick(provider);
const remote = await quickpick.pick();
let url: string | undefined;
if (remote) {
if (typeof remote.url === 'string') {
url = remote.url;
} else if (remote.url.length > 0) {
url = await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") });
}
}
if (!url || !options.branch) {
return url;
}
if (!provider.getBranches) {
return { url };
}
const branches = await provider.getBranches(url);
if (!branches) {
return { url };
}
const branch = await window.showQuickPick(branches, {
placeHolder: localize('branch name', "Branch name")
});
if (!branch) {
return { url };
}
return { url, branch };
export async function pickRemoteSource(options: PickRemoteSourceOptions & { branch?: false | undefined }): Promise<string | undefined>;
export async function pickRemoteSource(options: PickRemoteSourceOptions & { branch: true }): Promise<PickRemoteSourceResult | undefined>;
export async function pickRemoteSource(options: PickRemoteSourceOptions = {}): Promise<string | PickRemoteSourceResult | undefined> {
return GitBaseApi.getAPI().pickRemoteSource(options);
}

View File

@@ -5,7 +5,8 @@
import * as fs from 'fs';
import * as path from 'path';
import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands } from 'vscode';
import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode';
import TelemetryReporter from '@vscode/extension-telemetry';
import * as nls from 'vscode-nls';
import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git';
import { AutoFetcher } from './autofetch';
@@ -13,12 +14,13 @@ import { debounce, memoize, throttle } from './decorators';
import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git';
import { StatusBarCommands } from './statusbar';
import { toGitUri } from './uri';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, logTimestamp, onceEvent, pathEquals, relativePath } from './util';
import { IFileWatcher, watch } from './watch';
import { Log, LogLevel } from './log';
import { IRemoteSourceProviderRegistry } from './remoteProvider';
import { IPushErrorHandlerRegistry } from './pushError';
import { ApiRepository } from './api/api1';
import { IRemoteSourcePublisherRegistry } from './remotePublisher';
import { ActionButtonCommand } from './actionButton';
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
@@ -516,8 +518,8 @@ class FileEventLogger {
}
this.eventDisposable = combinedDisposable([
this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`[debug] [wt] Change: ${uri.fsPath}`)),
this.onDotGitFileChange(uri => this.outputChannel.appendLine(`[debug] [.git] Change: ${uri.fsPath}`))
this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [wt] Change: ${uri.fsPath}`)),
this.onDotGitFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [.git] Change: ${uri.fsPath}`))
]);
}
@@ -539,10 +541,12 @@ class DotGitWatcher implements IFileWatcher {
private repository: Repository,
private outputChannel: OutputChannel
) {
const rootWatcher = watch(repository.dotGit);
const rootWatcher = watch(repository.dotGit.path);
this.disposables.push(rootWatcher);
const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$/.test(uri.path));
// Ignore changes to the "index.lock" file, and watchman fsmonitor hook (https://git-scm.com/docs/githooks#_fsmonitor_watchman) cookie files.
// Watchman creates a cookie file inside the git directory whenever a query is run (https://facebook.github.io/watchman/docs/cookies.html).
const filteredRootWatcher = filterEvent(rootWatcher.event, uri => !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path));
this.event = anyEvent(filteredRootWatcher, this.emitter.event);
repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables);
@@ -559,7 +563,7 @@ class DotGitWatcher implements IFileWatcher {
this.transientDisposables = dispose(this.transientDisposables);
const { name, remote } = this.repository.HEAD.upstream;
const upstreamPath = path.join(this.repository.dotGit, 'refs', 'remotes', remote, name);
const upstreamPath = path.join(this.repository.dotGit.commonPath ?? this.repository.dotGit.path, 'refs', 'remotes', remote, name);
try {
const upstreamWatcher = watch(upstreamPath);
@@ -567,7 +571,7 @@ class DotGitWatcher implements IFileWatcher {
upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
} catch (err) {
if (Log.logLevel <= LogLevel.Error) {
this.outputChannel.appendLine(`Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`);
this.outputChannel.appendLine(`${logTimestamp()} Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`);
}
}
}
@@ -664,7 +668,7 @@ class ResourceCommandResolver {
case Status.MODIFIED:
case Status.UNTRACKED:
case Status.IGNORED:
case Status.INTENT_TO_ADD:
case Status.INTENT_TO_ADD: {
const uriString = resource.resourceUri.toString();
const [indexStatus] = this.repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString);
@@ -673,7 +677,7 @@ class ResourceCommandResolver {
}
return resource.resourceUri;
}
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return resource.resourceUri;
@@ -838,7 +842,7 @@ export class Repository implements Disposable {
return this.repository.root;
}
get dotGit(): string {
get dotGit(): { path: string; commonPath?: string } {
return this.repository.dotGit;
}
@@ -850,42 +854,42 @@ export class Repository implements Disposable {
constructor(
private readonly repository: BaseRepository,
remoteSourceProviderRegistry: IRemoteSourceProviderRegistry,
private pushErrorHandlerRegistry: IPushErrorHandlerRegistry,
remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry,
globalState: Memento,
outputChannel: OutputChannel
outputChannel: OutputChannel,
private telemetryReporter: TelemetryReporter
) {
const workspaceWatcher = workspace.createFileSystemWatcher('**');
this.disposables.push(workspaceWatcher);
const repositoryWatcher = workspace.createFileSystemWatcher(new RelativePattern(Uri.file(repository.root), '**'));
this.disposables.push(repositoryWatcher);
const onWorkspaceFileChange = anyEvent(workspaceWatcher.onDidChange, workspaceWatcher.onDidCreate, workspaceWatcher.onDidDelete);
const onWorkspaceRepositoryFileChange = filterEvent(onWorkspaceFileChange, uri => isDescendant(repository.root, uri.fsPath));
const onWorkspaceWorkingTreeFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => !/\/\.git($|\/)/.test(uri.path));
const onRepositoryFileChange = anyEvent(repositoryWatcher.onDidChange, repositoryWatcher.onDidCreate, repositoryWatcher.onDidDelete);
const onRepositoryWorkingTreeFileChange = filterEvent(onRepositoryFileChange, uri => !/\.git($|\/)/.test(relativePath(repository.root, uri.fsPath)));
let onDotGitFileChange: Event<Uri>;
let onRepositoryDotGitFileChange: Event<Uri>;
try {
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
onDotGitFileChange = dotGitFileWatcher.event;
onRepositoryDotGitFileChange = dotGitFileWatcher.event;
this.disposables.push(dotGitFileWatcher);
} catch (err) {
if (Log.logLevel <= LogLevel.Error) {
outputChannel.appendLine(`Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`);
outputChannel.appendLine(`${logTimestamp()} Failed to watch path:'${this.dotGit.path}' or commonPath:'${this.dotGit.commonPath}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`);
}
onDotGitFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => /\/\.git($|\/)/.test(uri.path));
onRepositoryDotGitFileChange = filterEvent(onRepositoryFileChange, uri => /\.git($|\/)/.test(uri.path));
}
// FS changes should trigger `git status`:
// - any change inside the repository working tree
// - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock`
const onFileChange = anyEvent(onWorkspaceWorkingTreeFileChange, onDotGitFileChange);
const onFileChange = anyEvent(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange);
onFileChange(this.onFileChange, this, this.disposables);
// Relevate repository changes should trigger virtual document change events
onDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
onRepositoryDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
this.disposables.push(new FileEventLogger(onWorkspaceWorkingTreeFileChange, onDotGitFileChange, outputChannel));
this.disposables.push(new FileEventLogger(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange, outputChannel));
const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root);
@@ -959,11 +963,16 @@ export class Repository implements Disposable {
}
}, null, this.disposables);
const statusBar = new StatusBarCommands(this, remoteSourceProviderRegistry);
const statusBar = new StatusBarCommands(this, remoteSourcePublisherRegistry);
this.disposables.push(statusBar);
statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables);
this._sourceControl.statusBarCommands = statusBar.commands;
const actionButton = new ActionButtonCommand(this);
this.disposables.push(actionButton);
actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button);
this._sourceControl.actionButton = actionButton.button;
const progressManager = new ProgressManager(this);
this.disposables.push(progressManager);
@@ -1069,7 +1078,7 @@ export class Repository implements Disposable {
return await this.repository.getCommitTemplate();
}
getConfigs(): Promise<{ key: string; value: string; }[]> {
getConfigs(): Promise<{ key: string; value: string }[]> {
return this.run(Operation.Config, () => this.repository.getConfigs('local'));
}
@@ -1150,8 +1159,11 @@ export class Repository implements Disposable {
return this.run(Operation.HashObject, () => this.repository.hashObject(data));
}
async add(resources: Uri[], opts?: { update?: boolean; }): Promise<void> {
await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.fsPath), opts));
async add(resources: Uri[], opts?: { update?: boolean }): Promise<void> {
await this.run(Operation.Add, async () => {
await this.repository.add(resources.map(r => r.fsPath), opts);
this.closeDiffEditors([], [...resources.map(r => r.fsPath)]);
});
}
async rm(resources: Uri[]): Promise<void> {
@@ -1159,16 +1171,28 @@ export class Repository implements Disposable {
}
async stage(resource: Uri, contents: string): Promise<void> {
const relativePath = path.relative(this.repository.root, resource.fsPath).replace(/\\/g, '/');
await this.run(Operation.Stage, () => this.repository.stage(relativePath, contents));
const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/');
await this.run(Operation.Stage, async () => {
await this.repository.stage(path, contents);
this.closeDiffEditors([], [...resource.fsPath]);
});
this._onDidChangeOriginalResource.fire(resource);
}
async revert(resources: Uri[]): Promise<void> {
await this.run(Operation.RevertFiles, () => this.repository.revert('HEAD', resources.map(r => r.fsPath)));
await this.run(Operation.RevertFiles, async () => {
await this.repository.revert('HEAD', resources.map(r => r.fsPath));
this.closeDiffEditors([...resources.length !== 0 ?
resources.map(r => r.fsPath) :
this.indexGroup.resourceStates.map(r => r.resourceUri.fsPath)], []);
});
}
async commit(message: string | undefined, opts: CommitOptions = Object.create(null)): Promise<void> {
const indexResources = [...this.indexGroup.resourceStates.map(r => r.resourceUri.fsPath)];
const workingGroupResources = opts.all && opts.all !== 'tracked' ?
[...this.workingTreeGroup.resourceStates.map(r => r.resourceUri.fsPath)] : [];
if (this.rebaseCommit) {
await this.run(Operation.RebaseContinue, async () => {
if (opts.all) {
@@ -1177,6 +1201,7 @@ export class Repository implements Disposable {
}
await this.repository.rebaseContinue();
this.closeDiffEditors(indexResources, workingGroupResources);
});
} else {
await this.run(Operation.Commit, async () => {
@@ -1193,6 +1218,7 @@ export class Repository implements Disposable {
}
await this.repository.commit(message, opts);
this.closeDiffEditors(indexResources, workingGroupResources);
});
}
}
@@ -1236,9 +1262,35 @@ export class Repository implements Disposable {
await this.repository.clean(toClean);
await this.repository.checkout('', toCheckout);
await this.repository.updateSubmodules(submodulesToUpdate);
this.closeDiffEditors([], [...toClean, ...toCheckout]);
});
}
closeDiffEditors(indexResources: string[], workingTreeResources: string[], ignoreSetting: boolean = false): void {
const config = workspace.getConfiguration('git', Uri.file(this.root));
if (!config.get<boolean>('closeDiffOnOperation', false) && !ignoreSetting) { return; }
const diffEditorTabsToClose: Tab[] = [];
for (const tab of window.tabGroups.all.map(g => g.tabs).flat()) {
const { input } = tab;
if (input instanceof TabInputTextDiff || input instanceof TabInputNotebookDiff) {
if (input.modified.scheme === 'git' && indexResources.some(r => pathEquals(r, input.modified.fsPath))) {
// Index
diffEditorTabsToClose.push(tab);
}
if (input.modified.scheme === 'file' && input.original.scheme === 'git' && workingTreeResources.some(r => pathEquals(r, input.modified.fsPath))) {
// Working Tree
diffEditorTabsToClose.push(tab);
}
}
}
// Close editors
window.tabGroups.close(diffEditorTabsToClose, true);
}
async branch(name: string, _checkout: boolean, _ref?: string): Promise<void> {
await this.run(Operation.Branch, () => this.repository.branch(name, _checkout, _ref));
}
@@ -1287,11 +1339,11 @@ export class Repository implements Disposable {
await this.run(Operation.DeleteTag, () => this.repository.deleteTag(name));
}
async checkout(treeish: string, opts?: { detached?: boolean; }): Promise<void> {
async checkout(treeish: string, opts?: { detached?: boolean }): Promise<void> {
await this.run(Operation.Checkout, () => this.repository.checkout(treeish, [], opts));
}
async checkoutTracking(treeish: string, opts: { detached?: boolean; } = {}): Promise<void> {
async checkoutTracking(treeish: string, opts: { detached?: boolean } = {}): Promise<void> {
await this.run(Operation.CheckoutTracking, () => this.repository.checkout(treeish, [], { ...opts, track: true }));
}
@@ -1324,7 +1376,7 @@ export class Repository implements Disposable {
}
@throttle
async fetchDefault(options: { silent?: boolean; } = {}): Promise<void> {
async fetchDefault(options: { silent?: boolean } = {}): Promise<void> {
await this._fetch({ silent: options.silent });
}
@@ -1342,7 +1394,7 @@ export class Repository implements Disposable {
await this._fetch(options);
}
private async _fetch(options: { remote?: string, ref?: string, all?: boolean, prune?: boolean, depth?: number, silent?: boolean; } = {}): Promise<void> {
private async _fetch(options: { remote?: string; ref?: string; all?: boolean; prune?: boolean; depth?: number; silent?: boolean } = {}): Promise<void> {
if (!options.prune) {
const config = workspace.getConfiguration('git', Uri.file(this.root));
const prune = config.get<boolean>('pruneOnFetch');
@@ -1543,16 +1595,16 @@ export class Repository implements Disposable {
async show(ref: string, filePath: string): Promise<string> {
return await this.run(Operation.Show, async () => {
const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/');
const configFiles = workspace.getConfiguration('files', Uri.file(filePath));
const defaultEncoding = configFiles.get<string>('encoding');
const autoGuessEncoding = configFiles.get<boolean>('autoGuessEncoding');
try {
return await this.repository.bufferString(`${ref}:${relativePath}`, defaultEncoding, autoGuessEncoding);
return await this.repository.bufferString(`${ref}:${path}`, defaultEncoding, autoGuessEncoding);
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.WrongCase) {
const gitRelativePath = await this.repository.getGitRelativePath(ref, relativePath);
const gitRelativePath = await this.repository.getGitRelativePath(ref, path);
return await this.repository.bufferString(`${ref}:${gitRelativePath}`, defaultEncoding, autoGuessEncoding);
}
@@ -1563,16 +1615,16 @@ export class Repository implements Disposable {
async buffer(ref: string, filePath: string): Promise<Buffer> {
return this.run(Operation.Show, () => {
const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
return this.repository.buffer(`${ref}:${relativePath}`);
const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/');
return this.repository.buffer(`${ref}:${path}`);
});
}
getObjectDetails(ref: string, filePath: string): Promise<{ mode: string, object: string, size: number; }> {
getObjectDetails(ref: string, filePath: string): Promise<{ mode: string; object: string; size: number }> {
return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, filePath));
}
detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string; }> {
detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> {
return this.run(Operation.Show, () => this.repository.detectObjectType(object));
}
@@ -1585,7 +1637,15 @@ export class Repository implements Disposable {
}
async createStash(message?: string, includeUntracked?: boolean): Promise<void> {
return await this.run(Operation.Stash, () => this.repository.createStash(message, includeUntracked));
const indexResources = [...this.indexGroup.resourceStates.map(r => r.resourceUri.fsPath)];
const workingGroupResources = [
...this.workingTreeGroup.resourceStates.map(r => r.resourceUri.fsPath),
...includeUntracked ? this.untrackedGroup.resourceStates.map(r => r.resourceUri.fsPath) : []];
return await this.run(Operation.Stash, async () => {
this.repository.createStash(message, includeUntracked);
this.closeDiffEditors(indexResources, workingGroupResources);
});
}
async popStash(index?: number): Promise<void> {
@@ -1608,7 +1668,7 @@ export class Repository implements Disposable {
return await this.run(Operation.Ignore, async () => {
const ignoreFile = `${this.repository.root}${path.sep}.gitignore`;
const textToAppend = files
.map(uri => path.relative(this.repository.root, uri.fsPath).replace(/\\/g, '/'))
.map(uri => relativePath(this.repository.root, uri.fsPath).replace(/\\/g, '/'))
.join('\n');
const document = await new Promise(c => fs.exists(ignoreFile, c))
@@ -1798,11 +1858,23 @@ export class Repository implements Disposable {
@throttle
private async updateModelState(): Promise<void> {
const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
const untrackedChanges = scopedConfig.get<'mixed' | 'separate' | 'hidden'>('untrackedChanges');
const ignoreSubmodules = scopedConfig.get<boolean>('ignoreSubmodules');
const limit = scopedConfig.get<number>('statusLimit', 5000);
const limit = scopedConfig.get<number>('statusLimit', 10000);
const { status, didHitLimit } = await this.repository.getStatus({ limit, ignoreSubmodules });
const { status, statusLength, didHitLimit } = await this.repository.getStatus({ limit, ignoreSubmodules, untrackedChanges });
if (didHitLimit) {
/* __GDPR__
"statusLimit" : {
"ignoreSubmodules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"limit": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"statusLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryReporter.sendTelemetryEvent('statusLimit', { ignoreSubmodules: String(ignoreSubmodules) }, { limit, statusLength });
}
const config = workspace.getConfiguration('git');
const shouldIgnore = config.get<boolean>('ignoreLimitWarning') === true;
@@ -1873,8 +1945,6 @@ export class Repository implements Disposable {
this._submodules = submodules!;
this.rebaseCommit = rebaseCommit;
const untrackedChanges = scopedConfig.get<'mixed' | 'separate' | 'hidden'>('untrackedChanges');
const index: Resource[] = [];
const workingTree: Resource[] = [];
const merge: Resource[] = [];
@@ -1923,37 +1993,6 @@ export class Repository implements Disposable {
return undefined;
});
let actionButton: SourceControl['actionButton'];
if (HEAD !== undefined) {
const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
const showActionButton = config.get<string>('showUnpublishedCommitsButton', 'whenEmpty');
if (showActionButton === 'always' || (showActionButton === 'whenEmpty' && workingTree.length === 0 && index.length === 0 && untracked.length === 0 && merge.length === 0)) {
if (HEAD.name && HEAD.commit) {
if (HEAD.upstream) {
if (HEAD.ahead) {
const rebaseWhenSync = config.get<string>('rebaseWhenSync');
actionButton = {
command: rebaseWhenSync ? 'git.syncRebase' : 'git.sync',
title: localize('scm button sync title', ' Sync Changes $(sync){0}{1}', HEAD.behind ? `${HEAD.behind}$(arrow-down) ` : '', `${HEAD.ahead}$(arrow-up)`),
tooltip: this.syncTooltip,
arguments: [this._sourceControl],
};
}
} else {
actionButton = {
command: 'git.publish',
title: localize('scm button publish title', "$(cloud-upload) Publish Changes"),
tooltip: localize('scm button publish tooltip', "Publish Changes"),
arguments: [this._sourceControl],
};
}
}
}
}
this._sourceControl.actionButton = actionButton;
// set resource groups
this.mergeGroup.resourceStates = merge;
this.indexGroup.resourceStates = index;
@@ -1963,9 +2002,6 @@ export class Repository implements Disposable {
// set count badge
this.setCountBadge();
// Update context key with changed resources
commands.executeCommand('setContext', 'git.changedResources', [...merge, ...index, ...workingTree, ...untracked].map(r => r.resourceUri.fsPath.toString()));
this._onDidChangeStatus.fire();
this._sourceControl.commitTemplate = await this.getInputTemplate();

View File

@@ -49,7 +49,7 @@ export function applyLineChanges(original: TextDocument, modified: TextDocument,
return result.join('');
}
export function toLineRanges(selections: Selection[], textDocument: TextDocument): Range[] {
export function toLineRanges(selections: readonly Selection[], textDocument: TextDocument): Range[] {
const lineRanges = selections.map(s => {
const startLine = textDocument.lineAt(s.start.line);
const endLine = textDocument.lineAt(s.end.line);
@@ -109,12 +109,28 @@ export function intersectDiffWithRange(textDocument: TextDocument, diff: LineCha
if (diff.modifiedEndLineNumber === 0) {
return diff;
} else {
return {
originalStartLineNumber: diff.originalStartLineNumber,
originalEndLineNumber: diff.originalEndLineNumber,
modifiedStartLineNumber: intersection.start.line + 1,
modifiedEndLineNumber: intersection.end.line + 1
};
const modifiedStartLineNumber = intersection.start.line + 1;
const modifiedEndLineNumber = intersection.end.line + 1;
// heuristic: same number of lines on both sides, let's assume line by line
if (diff.originalEndLineNumber - diff.originalStartLineNumber === diff.modifiedEndLineNumber - diff.modifiedStartLineNumber) {
const delta = modifiedStartLineNumber - diff.modifiedStartLineNumber;
const length = modifiedEndLineNumber - modifiedStartLineNumber;
return {
originalStartLineNumber: diff.originalStartLineNumber + delta,
originalEndLineNumber: diff.originalStartLineNumber + delta + length,
modifiedStartLineNumber,
modifiedEndLineNumber
};
} else {
return {
originalStartLineNumber: diff.originalStartLineNumber,
originalEndLineNumber: diff.originalEndLineNumber,
modifiedStartLineNumber,
modifiedEndLineNumber
};
}
}
}

View File

@@ -7,8 +7,8 @@ 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';
import { Branch, RemoteSourcePublisher } from './api/git';
import { IRemoteSourcePublisherRegistry } from './remotePublisher';
const localize = nls.loadMessageBundle();
@@ -44,7 +44,7 @@ interface SyncStatusBarState {
readonly isSyncRunning: boolean;
readonly hasRemotes: boolean;
readonly HEAD: Branch | undefined;
readonly remoteSourceProviders: RemoteSourceProvider[];
readonly remoteSourcePublishers: RemoteSourcePublisher[];
}
class SyncStatusBar {
@@ -60,21 +60,20 @@ class SyncStatusBar {
this._onDidChange.fire();
}
constructor(private repository: Repository, private remoteSourceProviderRegistry: IRemoteSourceProviderRegistry) {
constructor(private repository: Repository, private remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry) {
this._state = {
enabled: true,
isSyncRunning: false,
hasRemotes: false,
HEAD: undefined,
remoteSourceProviders: this.remoteSourceProviderRegistry.getRemoteProviders()
.filter(p => !!p.publishRepository)
remoteSourcePublishers: remoteSourcePublisherRegistry.getRemoteSourcePublishers()
};
repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables);
repository.onDidChangeOperations(this.onDidChangeOperations, this, this.disposables);
anyEvent(remoteSourceProviderRegistry.onDidAddRemoteSourceProvider, remoteSourceProviderRegistry.onDidRemoveRemoteSourceProvider)
(this.onDidChangeRemoteSourceProviders, this, this.disposables);
anyEvent(remoteSourcePublisherRegistry.onDidAddRemoteSourcePublisher, remoteSourcePublisherRegistry.onDidRemoveRemoteSourcePublisher)
(this.onDidChangeRemoteSourcePublishers, this, this.disposables);
const onEnablementChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.enableStatusBarSync'));
onEnablementChange(this.updateEnablement, this, this.disposables);
@@ -104,11 +103,10 @@ class SyncStatusBar {
};
}
private onDidChangeRemoteSourceProviders(): void {
private onDidChangeRemoteSourcePublishers(): void {
this.state = {
...this.state,
remoteSourceProviders: this.remoteSourceProviderRegistry.getRemoteProviders()
.filter(p => !!p.publishRepository)
remoteSourcePublishers: this.remoteSourcePublisherRegistry.getRemoteSourcePublishers()
};
}
@@ -118,12 +116,12 @@ class SyncStatusBar {
}
if (!this.state.hasRemotes) {
if (this.state.remoteSourceProviders.length === 0) {
if (this.state.remoteSourcePublishers.length === 0) {
return;
}
const tooltip = this.state.remoteSourceProviders.length === 1
? localize('publish to', "Publish to {0}", this.state.remoteSourceProviders[0].name)
const tooltip = this.state.remoteSourcePublishers.length === 1
? localize('publish to', "Publish to {0}", this.state.remoteSourcePublishers[0].name)
: localize('publish to...', "Publish to...");
return {
@@ -154,7 +152,7 @@ class SyncStatusBar {
} else {
icon = '$(cloud-upload)';
command = 'git.publish';
tooltip = localize('publish changes', "Publish Changes");
tooltip = localize('publish branch', "Publish Branch");
}
} else {
command = '';
@@ -188,8 +186,8 @@ export class StatusBarCommands {
private checkoutStatusBar: CheckoutStatusBar;
private disposables: Disposable[] = [];
constructor(repository: Repository, remoteSourceProviderRegistry: IRemoteSourceProviderRegistry) {
this.syncStatusBar = new SyncStatusBar(repository, remoteSourceProviderRegistry);
constructor(repository: Repository, remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry) {
this.syncStatusBar = new SyncStatusBar(repository, remoteSourcePublisherRegistry);
this.checkoutStatusBar = new CheckoutStatusBar(repository);
this.onDidChange = anyEvent(this.syncStatusBar.onDidChange, this.checkoutStatusBar.onDidChange);
}

View File

@@ -42,13 +42,12 @@ suite('git smoke test', function () {
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 init -b main', { cwd });
cp.execSync('git config user.name testuser', { cwd });
cp.execSync('git config user.email monacotools@microsoft.com', { cwd });
cp.execSync('git config commit.gpgsign false', { cwd });
cp.execSync('git add .', { cwd });
cp.execSync('git commit -m "initial commit"', { cwd });
cp.execSync('git branch -m main', { cwd });
// make sure git is activated
const ext = extensions.getExtension<GitExtension>('vscode.git');

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vscode-nls';
import { CancellationToken, ConfigurationChangeEvent, Disposable, env, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace } from 'vscode';
import { CancellationToken, ConfigurationChangeEvent, Disposable, env, Event, EventEmitter, MarkdownString, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace } from 'vscode';
import { Model } from './model';
import { Repository, Resource } from './repository';
import { debounce } from './decorators';
@@ -50,6 +50,20 @@ export class GitTimelineItem extends TimelineItem {
return this.shortenRef(this.previousRef);
}
setItemDetails(author: string, email: string | undefined, date: string, message: string): void {
this.tooltip = new MarkdownString('', true);
if (email) {
const emailTitle = localize('git.timeline.email', "Email");
this.tooltip.appendMarkdown(`$(account) [**${author}**](mailto:${email} "${emailTitle} ${author}")\n\n`);
} else {
this.tooltip.appendMarkdown(`$(account) **${author}**\n\n`);
}
this.tooltip.appendMarkdown(`$(history) ${date}\n\n`);
this.tooltip.appendMarkdown(message);
}
private shortenRef(ref: string): string {
if (ref === '' || ref === '~' || ref === 'HEAD') {
return ref;
@@ -155,6 +169,9 @@ export class GitTimelineProvider implements TimelineProvider {
const dateType = config.get<'committed' | 'authored'>('date');
const showAuthor = config.get<boolean>('showAuthor');
const showUncommitted = config.get<boolean>('showUncommitted');
const openComparison = localize('git.timeline.openComparison', "Open Comparison");
const items = commits.map<GitTimelineItem>((c, i) => {
const date = dateType === 'authored' ? c.authorDate : c.commitDate;
@@ -166,12 +183,13 @@ export class GitTimelineProvider implements TimelineProvider {
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.setItemDetails(c.authorName!, c.authorEmail, dateFormatter.format(date), message);
const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri);
if (cmd) {
item.command = {
title: 'Open Comparison',
title: openComparison,
command: cmd.command,
arguments: cmd.arguments,
};
@@ -191,12 +209,12 @@ export class GitTimelineProvider implements TimelineProvider {
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new ThemeIcon('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.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type));
const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri);
if (cmd) {
item.command = {
title: 'Open Comparison',
title: openComparison,
command: cmd.command,
arguments: cmd.arguments,
};
@@ -205,26 +223,27 @@ export class GitTimelineProvider implements TimelineProvider {
items.splice(0, 0, item);
}
const working = repo.workingTreeGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath);
if (working) {
const date = new Date();
if (showUncommitted) {
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', 'Uncommitted Changes'), date.getTime(), 'working', 'git:file:working');
// TODO@eamodio: Replace with a better icon -- reflecting its status maybe?
item.iconPath = new ThemeIcon('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));
const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommitted Changes'), date.getTime(), 'working', 'git:file:working');
item.iconPath = new ThemeIcon('circle-outline');
item.description = '';
item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type));
const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri);
if (cmd) {
item.command = {
title: 'Open Comparison',
command: cmd.command,
arguments: cmd.arguments,
};
const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri);
if (cmd) {
item.command = {
title: openComparison,
command: cmd.command,
arguments: cmd.arguments,
};
}
items.splice(0, 0, item);
}
items.splice(0, 0, item);
}
}
@@ -236,12 +255,12 @@ export class GitTimelineProvider implements TimelineProvider {
private ensureProviderRegistration() {
if (this.providerDisposable === undefined) {
this.providerDisposable = workspace.registerTimelineProvider(['file', 'git', 'vscode-remote', 'gitlens-git'], this);
this.providerDisposable = workspace.registerTimelineProvider(['file', 'git', 'vscode-remote', 'gitlens-git', 'vscode-local-history'], this);
}
}
private onConfigurationChanged(e: ConfigurationChangeEvent) {
if (e.affectsConfiguration('git.timeline.date') || e.affectsConfiguration('git.timeline.showAuthor')) {
if (e.affectsConfiguration('git.timeline.date') || e.affectsConfiguration('git.timeline.showAuthor') || e.affectsConfiguration('git.timeline.showUncommitted')) {
this.fireChanged();
}
}

View File

@@ -1,8 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. 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" />

View File

@@ -4,15 +4,22 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Disposable, EventEmitter } from 'vscode';
import { dirname, sep } from 'path';
import { dirname, sep, relative } from 'path';
import { Readable } from 'stream';
import { promises as fs, createReadStream } from 'fs';
import * as byline from 'byline';
export const isMacintosh = process.platform === 'darwin';
export const isWindows = process.platform === 'win32';
export function log(...args: any[]): void {
console.log.apply(console, ['git:', ...args]);
}
export function logTimestamp(): string {
return `[${new Date().toISOString()}]`;
}
export interface IDisposable {
dispose(): void;
}
@@ -168,7 +175,7 @@ export async function mkdirp(path: string, mode?: number): Promise<boolean> {
}
export function uniqueFilter<T>(keyFn: (t: T) => string): (t: T) => boolean {
const seen: { [key: string]: boolean; } = Object.create(null);
const seen: { [key: string]: boolean } = Object.create(null);
return element => {
const key = keyFn(element);
@@ -280,8 +287,14 @@ export function detectUnicodeEncoding(buffer: Buffer): Encoding | null {
return null;
}
function isWindowsPath(path: string): boolean {
return /^[a-zA-Z]:\\/.test(path);
function normalizePath(path: string): string {
// Windows & Mac are currently being handled
// as case insensitive file systems in VS Code.
if (isWindows || isMacintosh) {
return path.toLowerCase();
}
return path;
}
export function isDescendant(parent: string, descendant: string): boolean {
@@ -293,23 +306,26 @@ export function isDescendant(parent: string, descendant: string): boolean {
parent += sep;
}
// Windows is case insensitive
if (isWindowsPath(parent)) {
parent = parent.toLowerCase();
descendant = descendant.toLowerCase();
}
return descendant.startsWith(parent);
return normalizePath(descendant).startsWith(normalizePath(parent));
}
export function pathEquals(a: string, b: string): boolean {
// Windows is case insensitive
if (isWindowsPath(a)) {
a = a.toLowerCase();
b = b.toLowerCase();
return normalizePath(a) === normalizePath(b);
}
/**
* Given the `repository.root` compute the relative path while trying to preserve
* the casing of the resource URI. The `repository.root` segment of the path can
* have a casing mismatch if the folder/workspace is being opened with incorrect
* casing.
*/
export function relativePath(from: string, to: string): string {
if (isDescendant(from, to) && from.length < to.length) {
return to.substring(from.length + 1);
}
return a === b;
// Fallback to `path.relative`
return relative(from, to);
}
export function* splitInChunks(array: string[], maxChunkLength: number): IterableIterator<string[]> {
@@ -379,7 +395,7 @@ export class Limiter<T> {
}
}
type Completion<T> = { success: true, value: T } | { success: false, err: any };
type Completion<T> = { success: true; value: T } | { success: false; err: any };
export class PromiseSource<T> {

View File

@@ -3,23 +3,20 @@
* Licensed under the Source EULA. 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';
import { Event, RelativePattern, Uri, workspace } from 'vscode';
import { IDisposable, anyEvent } 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));
const watcher = workspace.createFileSystemWatcher(new RelativePattern(location, '*'));
return new class implements IFileWatcher {
event = onDotGitFileChangeEmitter.event;
dispose() { dotGitWatcher.close(); }
event = anyEvent(watcher.onDidCreate, watcher.onDidChange, watcher.onDidDelete);
dispose() {
watcher.dispose();
}
};
}

View File

@@ -1,160 +0,0 @@
{
"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"
}
]
}

View File

@@ -1,141 +0,0 @@
{
"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"
}
]
}
]
}
}
}

View File

@@ -1,61 +0,0 @@
{
"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"
}
]
}

View File

@@ -1,10 +0,0 @@
{
"name": "Ignore",
"scopeName": "source.ignore",
"patterns": [
{
"match": "^#.*",
"name": "comment.line.number-sign.ignore"
}
]
}

View File

@@ -1,13 +0,0 @@
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
#

View File

@@ -1,7 +0,0 @@
diff --git a/helloworld.txt b/helloworld.txt
index e4f37c4..557db03 100644
--- a/helloworld.txt
+++ b/helloworld.txt
@@ -1 +1 @@
-Hello world
+Hello World

View File

@@ -1,15 +0,0 @@
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

View File

@@ -1,255 +0,0 @@
[
{
"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"
}
}
]

View File

@@ -1,167 +0,0 @@
[
{
"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"
}
}
]

View File

@@ -1,541 +0,0 @@
[
{
"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"
}
}
]

View File

@@ -1 +0,0 @@
--ui tdd out/test

View File

@@ -8,6 +8,14 @@
]
},
"include": [
"src/**/*"
"src/**/*",
"../../src/vscode-dts/vscode.d.ts",
"../../src/vscode-dts/vscode.proposed.diffCommand.d.ts",
"../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts",
"../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts",
"../../src/vscode-dts/vscode.proposed.scmValidation.d.ts",
"../../src/vscode-dts/vscode.proposed.tabs.d.ts",
"../../src/vscode-dts/vscode.proposed.timeline.d.ts",
"../types/lib.textEncoder.d.ts"
]
}

View File

@@ -14,26 +14,36 @@
dependencies:
"@types/node" "*"
"@types/mocha@^8.2.0":
version "8.2.3"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.3.tgz#bbeb55fbc73f28ea6de601fbfa4613f58d785323"
integrity sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==
"@types/mocha@^9.1.1":
version "9.1.1"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4"
integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==
"@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@14.x":
version "14.14.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.43.tgz#26bcbb0595b305400e8ceaf9a127a7f905ae49c8"
integrity sha512-3pwDJjp1PWacPTpH0LcfhgjvurQvrZFBrC6xxjaUEZ7ifUtT32jtjPxEMMblpqd2Mvx+k8haqQJLQxolyGN/cQ==
"@types/node@16.x":
version "16.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
"@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=
"@vscode/extension-telemetry@0.4.10":
version "0.4.10"
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910"
integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w==
"@vscode/iconv-lite-umd@0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48"
integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==
byline@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
@@ -48,10 +58,6 @@ file-type@16.5.4:
strtok3 "^6.2.4"
token-types "^4.1.1"
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==
ieee754@^1.2.1:
version "1.2.1"
@@ -62,7 +68,6 @@ inherits@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -127,11 +132,6 @@ util-deprecate@^1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
vscode-extension-telemetry@0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.4.2.tgz#6ef847a80c9cfc207eb15e3a254f235acebb65a5"
integrity sha512-y0f51mVoFxHIzULQNCC26TBFIKdEC7uckS3tFoK++OOOl8mU2LlOxgmbd52T/SXoXNg5aI7xqs+4V2ug5ITvKw==
vscode-nls@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"