mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
Git - Improvements to opening git repositories in parent folders (#171617)
* Initial implementation for external repositories * Added setting * Add basic welcome views * Replaced "Always Open" with "Configure" * Remove code duplication * Polish based on feedback * Language consistency * Update notification severity * Move away from the "external repository" terminology * Refactor notification logic * Saving my changes * Further improvements * Refactor parent repository notification * Update message and fix edge case when setting is set to `never`
This commit is contained in:
parent
6894f821dc
commit
fe423bbdba
|
@ -712,6 +712,11 @@
|
|||
"command": "git.manageUnsafeRepositories",
|
||||
"title": "%command.manageUnsafeRepositories%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.openRepositoriesInParentFolders",
|
||||
"title": "%command.openRepositoriesInParentFolders%",
|
||||
"category": "Git"
|
||||
}
|
||||
],
|
||||
"continueEditSession": [
|
||||
|
@ -1162,6 +1167,10 @@
|
|||
{
|
||||
"command": "git.manageUnsafeRepositories",
|
||||
"when": "config.git.enabled && !git.missing && git.unsafeRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.openRepositoriesInParentFolders",
|
||||
"when": "config.git.enabled && !git.missing && git.parentRepositoryCount != 0"
|
||||
}
|
||||
],
|
||||
"scm/title": [
|
||||
|
@ -2674,6 +2683,22 @@
|
|||
"experimental"
|
||||
]
|
||||
},
|
||||
"git.openRepositoryInParentFolders": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"always",
|
||||
"never",
|
||||
"prompt"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%config.openRepositoryInParentFolders.always%",
|
||||
"%config.openRepositoryInParentFolders.never%",
|
||||
"%config.openRepositoryInParentFolders.prompt%"
|
||||
],
|
||||
"default": "prompt",
|
||||
"markdownDescription": "%config.openRepositoryInParentFolders%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.publishBeforeContinueOn": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
|
@ -2827,14 +2852,14 @@
|
|||
{
|
||||
"view": "scm",
|
||||
"contents": "%view.workbench.scm.empty%",
|
||||
"when": "config.git.enabled && !git.missing && workbenchState == empty && git.unsafeRepositoryCount == 0",
|
||||
"when": "config.git.enabled && !git.missing && workbenchState == empty && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0",
|
||||
"enablement": "git.state == initialized",
|
||||
"group": "2_open@1"
|
||||
},
|
||||
{
|
||||
"view": "scm",
|
||||
"contents": "%view.workbench.scm.emptyWorkspace%",
|
||||
"when": "config.git.enabled && !git.missing && workbenchState == workspace && workspaceFolderCount == 0 && git.unsafeRepositoryCount == 0",
|
||||
"when": "config.git.enabled && !git.missing && workbenchState == workspace && workspaceFolderCount == 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0",
|
||||
"enablement": "git.state == initialized",
|
||||
"group": "2_open@1"
|
||||
},
|
||||
|
@ -2851,15 +2876,25 @@
|
|||
{
|
||||
"view": "scm",
|
||||
"contents": "%view.workbench.scm.folder%",
|
||||
"when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == folder && scmRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && remoteName != 'codespaces'",
|
||||
"when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == folder && scmRepositoryCount == 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && remoteName != 'codespaces'",
|
||||
"group": "5_scm@1"
|
||||
},
|
||||
{
|
||||
"view": "scm",
|
||||
"contents": "%view.workbench.scm.workspace%",
|
||||
"when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0 && scmRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && remoteName != 'codespaces'",
|
||||
"when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0 && scmRepositoryCount == 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && remoteName != 'codespaces'",
|
||||
"group": "5_scm@1"
|
||||
},
|
||||
{
|
||||
"view": "scm",
|
||||
"contents": "%view.workbench.scm.repositoryInParentFolders%",
|
||||
"when": "config.git.enabled && !git.missing && git.state == initialized && git.parentRepositoryCount == 1"
|
||||
},
|
||||
{
|
||||
"view": "scm",
|
||||
"contents": "%view.workbench.scm.repositoriesInParentFolders%",
|
||||
"when": "config.git.enabled && !git.missing && git.state == initialized && git.parentRepositoryCount > 1"
|
||||
},
|
||||
{
|
||||
"view": "scm",
|
||||
"contents": "%view.workbench.scm.unsafeRepository%",
|
||||
|
@ -2873,15 +2908,13 @@
|
|||
{
|
||||
"view": "explorer",
|
||||
"contents": "%view.workbench.cloneRepository%",
|
||||
"when": "config.git.enabled",
|
||||
"enablement": "git.state == initialized",
|
||||
"when": "config.git.enabled && git.state == initialized && scmRepositoryCount == 0",
|
||||
"group": "5_scm@1"
|
||||
},
|
||||
{
|
||||
"view": "explorer",
|
||||
"contents": "%view.workbench.learnMore%",
|
||||
"when": "config.git.enabled",
|
||||
"enablement": "git.state == initialized",
|
||||
"when": "config.git.enabled && git.state == initialized && scmRepositoryCount == 0",
|
||||
"group": "5_scm@10"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
"command.timelineSelectForCompare": "Select for Compare",
|
||||
"command.timelineCompareWithSelected": "Compare with Selected",
|
||||
"command.manageUnsafeRepositories": "Manage Unsafe Repositories",
|
||||
"command.openRepositoriesInParentFolders": "Open Repositories In Parent Folders",
|
||||
"command.api.getRepositories": "Get Repositories",
|
||||
"command.api.getRepositoryState": "Get Repository State",
|
||||
"command.api.getRemoteSources": "Get Remote Sources",
|
||||
|
@ -242,6 +243,10 @@
|
|||
"config.useIntegratedAskPass": "Controls whether GIT_ASKPASS should be overwritten to use the integrated version.",
|
||||
"config.mergeEditor": "Open the merge editor for files that are currently under conflict.",
|
||||
"config.optimisticUpdate": "Controls whether to optimistically update the state of the Source Control view after running git commands.",
|
||||
"config.openRepositoryInParentFolders": "Control whether a repository in the parent folders of the workspace, open file(s) should be opened.",
|
||||
"config.openRepositoryInParentFolders.always": "Always open a repository in the parent folders of the workspace, open file(s).",
|
||||
"config.openRepositoryInParentFolders.never": "Never open a repository in the parent folders of the workspace, open file(s).",
|
||||
"config.openRepositoryInParentFolders.prompt": "Prompt before opening a repository in the parent folders of the workspace, open file(s).",
|
||||
"config.publishBeforeContinueOn": "Controls whether to publish unpublished git state when using Continue Working On from a git repository.",
|
||||
"submenu.explorer": "Git",
|
||||
"submenu.commit": "Commit",
|
||||
|
@ -334,6 +339,24 @@
|
|||
"view.workbench.scm.scanWorkspaceForRepositories": {
|
||||
"message": "Scanning workspace for git repositories..."
|
||||
},
|
||||
"view.workbench.scm.repositoryInParentFolders": {
|
||||
"message": "A git repository was found in one of the parent directories of the workspace, open file(s).\n[Open Repository](command:git.openRepositoriesInParentFolders)\nUse [this setting](command:workbench.action.openSettings?%5B%22git.openRepositoryInParentFolders%22%5D) to control how git repositories in parent directories of the workspace, open file(s) are opened. To learn more [read our docs](https://aka.ms/vscode-git-external-repository).",
|
||||
"comment": [
|
||||
"{Locked='](command:git.openRepositoriesInParentFolders'}",
|
||||
"{Locked='](command:workbench.action.openSettings?%5B%22git.openRepositoryInParentFolders%22%5D'}",
|
||||
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code",
|
||||
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
|
||||
]
|
||||
},
|
||||
"view.workbench.scm.repositoriesInParentFolders": {
|
||||
"message": "Git repositories were found in one of the parent directories of the workspace, open file(s).\n[Open Repository](command:git.openRepositoriesInParentFolders)\nUse [this setting](command:workbench.action.openSettings?%5B%22git.openRepositoryInParentFolders%22%5D) to control how git repositories in parent directories of the workspace, open file(s) are opened. To learn more [read our docs](https://aka.ms/vscode-git-external-repository).",
|
||||
"comment": [
|
||||
"{Locked='](command:git.openRepositoriesInParentFolders'}",
|
||||
"{Locked='](command:workbench.action.openSettings?%5B%22git.openRepositoryInParentFolders%22%5D'}",
|
||||
"Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code",
|
||||
"Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links"
|
||||
]
|
||||
},
|
||||
"view.workbench.scm.unsafeRepository": {
|
||||
"message": "The detected git repository is potentially unsafe as the folder is owned by someone other than the current user.\n[Manage Unsafe Repositories](command:git.manageUnsafeRepositories)\nTo learn more about unsafe repositories [read our docs](https://aka.ms/vscode-git-unsafe-repository).",
|
||||
"comment": [
|
||||
|
|
|
@ -191,7 +191,7 @@ class FetchAllRemotesItem implements QuickPickItem {
|
|||
}
|
||||
}
|
||||
|
||||
class UnsafeRepositoryItem implements QuickPickItem {
|
||||
class RepositoryItem implements QuickPickItem {
|
||||
get label(): string {
|
||||
return `$(repo) ${this.path}`;
|
||||
}
|
||||
|
@ -3335,6 +3335,38 @@ export class CommandCenter {
|
|||
repository.closeDiffEditors(undefined, undefined, true);
|
||||
}
|
||||
|
||||
@command('git.openRepositoriesInParentFolders')
|
||||
async openRepositoriesInParentFolders(): Promise<void> {
|
||||
const parentRepositories: string[] = [];
|
||||
|
||||
const title = l10n.t('Open Repositories In Parent Folders');
|
||||
const placeHolder = l10n.t('Pick a repository to open');
|
||||
|
||||
const allRepositoriesLabel = l10n.t('All Repositories');
|
||||
const allRepositoriesQuickPickItem: QuickPickItem = { label: allRepositoriesLabel };
|
||||
const repositoriesQuickPickItems: QuickPickItem[] = Array.from(this.model.parentRepositories.keys()).sort().map(r => new RepositoryItem(r));
|
||||
|
||||
const items = this.model.parentRepositories.size === 1 ? [...repositoriesQuickPickItems] :
|
||||
[...repositoriesQuickPickItems, { label: '', kind: QuickPickItemKind.Separator }, allRepositoriesQuickPickItem];
|
||||
|
||||
const repositoryItem = await window.showQuickPick(items, { title, placeHolder });
|
||||
if (!repositoryItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (repositoryItem === allRepositoriesQuickPickItem) {
|
||||
// All Repositories
|
||||
parentRepositories.push(...this.model.parentRepositories.keys());
|
||||
} else {
|
||||
// One Repository
|
||||
parentRepositories.push((repositoryItem as RepositoryItem).path);
|
||||
}
|
||||
|
||||
for (const parentRepository of parentRepositories) {
|
||||
await this.model.openParentRepository(parentRepository);
|
||||
}
|
||||
}
|
||||
|
||||
@command('git.manageUnsafeRepositories')
|
||||
async manageUnsafeRepositories(): Promise<void> {
|
||||
const unsafeRepositories: string[] = [];
|
||||
|
@ -3345,13 +3377,13 @@ export class CommandCenter {
|
|||
|
||||
const allRepositoriesLabel = l10n.t('All Repositories');
|
||||
const allRepositoriesQuickPickItem: QuickPickItem = { label: allRepositoriesLabel };
|
||||
const repositoriesQuickPickItems: QuickPickItem[] = Array.from(this.model.unsafeRepositories.keys()).sort().map(r => new UnsafeRepositoryItem(r));
|
||||
const repositoriesQuickPickItems: QuickPickItem[] = Array.from(this.model.unsafeRepositories.keys()).sort().map(r => new RepositoryItem(r));
|
||||
|
||||
quickpick.items = this.model.unsafeRepositories.size === 1 ? [...repositoriesQuickPickItems] :
|
||||
[...repositoriesQuickPickItems, { label: '', kind: QuickPickItemKind.Separator }, allRepositoriesQuickPickItem];
|
||||
|
||||
quickpick.show();
|
||||
const repositoryItem = await new Promise<UnsafeRepositoryItem | QuickPickItem | undefined>(
|
||||
const repositoryItem = await new Promise<RepositoryItem | QuickPickItem | undefined>(
|
||||
resolve => {
|
||||
quickpick.onDidAccept(() => resolve(quickpick.activeItems[0]));
|
||||
quickpick.onDidHide(() => resolve(undefined));
|
||||
|
@ -3367,7 +3399,7 @@ export class CommandCenter {
|
|||
unsafeRepositories.push(...this.model.unsafeRepositories.keys());
|
||||
} else {
|
||||
// One Repository
|
||||
unsafeRepositories.push((repositoryItem as UnsafeRepositoryItem).path);
|
||||
unsafeRepositories.push((repositoryItem as RepositoryItem).path);
|
||||
}
|
||||
|
||||
for (const unsafeRepository of unsafeRepositories) {
|
||||
|
|
|
@ -34,18 +34,13 @@ class RepositoryPick implements QuickPickItem {
|
|||
constructor(public readonly repository: Repository, public readonly index: number) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* Key - normalized path used in user interface
|
||||
* Value - path extracted from the output of the `git status` command
|
||||
* used when calling `git config --global --add safe.directory`
|
||||
*/
|
||||
class UnsafeRepositoryMap extends Map<string, string> {
|
||||
abstract class RepositoryMap<T = void> extends Map<string, T> {
|
||||
constructor() {
|
||||
super();
|
||||
this.updateContextKey();
|
||||
}
|
||||
|
||||
override set(key: string, value: string): this {
|
||||
override set(key: string, value: T): this {
|
||||
const result = super.set(key, value);
|
||||
this.updateContextKey();
|
||||
|
||||
|
@ -59,11 +54,30 @@ class UnsafeRepositoryMap extends Map<string, string> {
|
|||
return result;
|
||||
}
|
||||
|
||||
private updateContextKey(): void {
|
||||
abstract updateContextKey(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key - normalized path used in user interface
|
||||
* Value - path extracted from the output of the `git status` command
|
||||
* used when calling `git config --global --add safe.directory`
|
||||
*/
|
||||
class UnsafeRepositoryMap extends RepositoryMap<string> {
|
||||
updateContextKey(): void {
|
||||
commands.executeCommand('setContext', 'git.unsafeRepositoryCount', this.size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Key - normalized path used in user interface
|
||||
* Value - value indicating whether the repository should be opened
|
||||
*/
|
||||
class ParentRepositoryMap extends RepositoryMap {
|
||||
updateContextKey(): void {
|
||||
commands.executeCommand('setContext', 'git.parentRepositoryCount', this.size);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ModelChangeEvent {
|
||||
repository: Repository;
|
||||
uri: Uri;
|
||||
|
@ -138,14 +152,18 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand
|
|||
private _onDidChangePostCommitCommandsProviders = new EventEmitter<void>();
|
||||
readonly onDidChangePostCommitCommandsProviders = this._onDidChangePostCommitCommandsProviders.event;
|
||||
|
||||
private showRepoOnHomeDriveRootWarning = true;
|
||||
private pushErrorHandlers = new Set<PushErrorHandler>();
|
||||
|
||||
private _unsafeRepositories = new UnsafeRepositoryMap();
|
||||
get unsafeRepositories(): Map<string, string> {
|
||||
get unsafeRepositories(): UnsafeRepositoryMap {
|
||||
return this._unsafeRepositories;
|
||||
}
|
||||
|
||||
private _parentRepositories = new ParentRepositoryMap();
|
||||
get parentRepositories(): ParentRepositoryMap {
|
||||
return this._parentRepositories;
|
||||
}
|
||||
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private logger: LogOutputChannel, private telemetryReporter: TelemetryReporter) {
|
||||
|
@ -168,6 +186,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand
|
|||
private async doInitialScan(): Promise<void> {
|
||||
const config = workspace.getConfiguration('git');
|
||||
const autoRepositoryDetection = config.get<boolean | 'subFolders' | 'openEditors'>('autoRepositoryDetection');
|
||||
const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');
|
||||
|
||||
const initialScanFn = () => Promise.all([
|
||||
this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }),
|
||||
|
@ -181,8 +200,12 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand
|
|||
await initialScanFn();
|
||||
}
|
||||
|
||||
// Unsafe repositories notification
|
||||
if (this._unsafeRepositories.size !== 0) {
|
||||
if (this._parentRepositories.size !== 0 &&
|
||||
parentRepositoryConfig === 'prompt') {
|
||||
// Parent repositories notification
|
||||
this.showParentRepositoryNotification();
|
||||
} else if (this._unsafeRepositories.size !== 0) {
|
||||
// Unsafe repositories notification
|
||||
this.showUnsafeRepositoryNotification();
|
||||
}
|
||||
|
||||
|
@ -394,65 +417,87 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand
|
|||
}
|
||||
|
||||
try {
|
||||
const rawRoot = await this.git.getRepositoryRoot(repoPath);
|
||||
|
||||
// This can happen whenever `path` has the wrong case sensitivity in
|
||||
// case insensitive file systems
|
||||
// https://github.com/microsoft/vscode/issues/33498
|
||||
const repositoryRoot = Uri.file(rawRoot).fsPath;
|
||||
this.logger.trace(`Repository root: ${repositoryRoot}`);
|
||||
const { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath);
|
||||
|
||||
if (this.getRepositoryExact(repositoryRoot)) {
|
||||
this.logger.trace(`Repository for path ${repositoryRoot} already exists`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.shouldRepositoryBeIgnored(rawRoot)) {
|
||||
if (this.shouldRepositoryBeIgnored(repositoryRoot)) {
|
||||
this.logger.trace(`Repository for path ${repositoryRoot} is ignored`);
|
||||
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))!!;
|
||||
// Handle git repositories that are in parent folders
|
||||
const isRepositoryOutsideWorkspace = (workspace.workspaceFolders ?? [])
|
||||
.find(f => pathEquals(f.uri.fsPath, repositoryRoot) || isDescendant(f.uri.fsPath, repositoryRoot)) === undefined;
|
||||
const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt');
|
||||
|
||||
if (!isRepoInWorkspaceFolders) {
|
||||
if (this.showRepoOnHomeDriveRootWarning) {
|
||||
window.showWarningMessage(l10n.t('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;
|
||||
if (isRepositoryOutsideWorkspace && parentRepositoryConfig !== 'always' && this.globalState.get<boolean>(`parentRepository:${repositoryRoot}`) !== true) {
|
||||
this.logger.trace(`Repository in parent folder: ${repositoryRoot}`);
|
||||
|
||||
if (!this._parentRepositories.has(repositoryRoot)) {
|
||||
// Show a notification if the parent repository is opened after the initial scan
|
||||
if (this.state === 'initialized' && parentRepositoryConfig === 'prompt') {
|
||||
this.showParentRepositoryNotification();
|
||||
}
|
||||
|
||||
this.logger.trace(`Repository for path ${repositoryRoot} is on the root of the HOMEDRIVE`);
|
||||
return;
|
||||
this._parentRepositories.set(repositoryRoot);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle unsafe repositories
|
||||
if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {
|
||||
this.logger.trace(`Unsafe repository: ${repositoryRoot}`);
|
||||
|
||||
// Show a notification if the unsafe repository is opened after the initial scan
|
||||
if (this._state === 'initialized' && !this._unsafeRepositories.has(repositoryRoot)) {
|
||||
this.showUnsafeRepositoryNotification();
|
||||
}
|
||||
|
||||
this._unsafeRepositories.set(repositoryRoot, unsafeRepositoryMatch[2]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Open repository
|
||||
const dotGit = await this.git.getRepositoryDotGit(repositoryRoot);
|
||||
const repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this.globalState, this.logger, this.telemetryReporter);
|
||||
|
||||
this.open(repository);
|
||||
repository.status(); // do not await this, we want SCM to know about the repo asap
|
||||
} catch (ex) {
|
||||
} catch (err) {
|
||||
// noop
|
||||
this.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
async openParentRepository(repoPath: string): Promise<void> {
|
||||
// Mark the repository to be opened from the parent folders
|
||||
this.globalState.update(`parentRepository:${repoPath}`, true);
|
||||
|
||||
await this.openRepository(repoPath);
|
||||
this.parentRepositories.delete(repoPath);
|
||||
}
|
||||
|
||||
private async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> {
|
||||
try {
|
||||
const rawRoot = await this.git.getRepositoryRoot(repoPath);
|
||||
|
||||
// This can happen whenever `path` has the wrong case sensitivity in case
|
||||
// insensitive file systems https://github.com/microsoft/vscode/issues/33498
|
||||
return { repositoryRoot: Uri.file(rawRoot).fsPath, unsafeRepositoryMatch: null };
|
||||
} catch (err) {
|
||||
// Handle unsafe repository
|
||||
const match = /^fatal: detected dubious ownership in repository at \'([^']+)\'[\s\S]*git config --global --add safe\.directory '?([^'\n]+)'?$/m.exec(ex.stderr);
|
||||
if (match && match.length === 3) {
|
||||
const unsafeRepositoryPath = path.normalize(match[1]);
|
||||
this.logger.trace(`Unsafe repository: ${unsafeRepositoryPath}`);
|
||||
|
||||
// Show a notification if the unsafe repository is opened after the initial repository scan
|
||||
if (this._state === 'initialized' && !this._unsafeRepositories.has(unsafeRepositoryPath)) {
|
||||
this.showUnsafeRepositoryNotification();
|
||||
}
|
||||
|
||||
this._unsafeRepositories.set(unsafeRepositoryPath, match[2]);
|
||||
|
||||
return;
|
||||
const unsafeRepositoryMatch = /^fatal: detected dubious ownership in repository at \'([^']+)\'[\s\S]*git config --global --add safe\.directory '?([^'\n]+)'?$/m.exec(err.stderr);
|
||||
if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) {
|
||||
return { repositoryRoot: path.normalize(unsafeRepositoryMatch[1]), unsafeRepositoryMatch };
|
||||
}
|
||||
|
||||
// noop
|
||||
this.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${ex}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -737,6 +782,36 @@ export class Model implements IRemoteSourcePublisherRegistry, IPostCommitCommand
|
|||
return [...this.pushErrorHandlers];
|
||||
}
|
||||
|
||||
private async showParentRepositoryNotification(): Promise<void> {
|
||||
const message = this.parentRepositories.size === 1 ?
|
||||
workspace.workspaceFolders !== undefined ?
|
||||
l10n.t('We found a git repository in one of the parent folders of this workspace. Would you like to open the repository?') :
|
||||
l10n.t('We found a git repository in one of the parent folders of the open file(s). Would you like to open the repository?') :
|
||||
workspace.workspaceFolders !== undefined ?
|
||||
l10n.t('We found git repositories in one of the parent folders of this workspace. Would you like to open the repositories?') :
|
||||
l10n.t('We found git repositories in one of the parent folders of the open file(s). Would you like to open the repositories?');
|
||||
|
||||
const yes = l10n.t('Yes');
|
||||
const always = l10n.t('Always');
|
||||
const never = l10n.t('Never');
|
||||
|
||||
const choice = await window.showWarningMessage(message, yes, always, never);
|
||||
if (choice === yes) {
|
||||
// Open Parent Repositories
|
||||
commands.executeCommand('git.openRepositoriesInParentFolders');
|
||||
} else if (choice === always || choice === never) {
|
||||
// Update setting
|
||||
const config = workspace.getConfiguration('git');
|
||||
await config.update('openRepositoryInParentFolders', choice === always ? 'always' : 'never', true);
|
||||
|
||||
if (choice === always) {
|
||||
for (const parentRepository of [...this.parentRepositories.keys()]) {
|
||||
await this.openParentRepository(parentRepository);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async showUnsafeRepositoryNotification(): Promise<void> {
|
||||
// If no repositories are open, we will use a welcome view to inform the user
|
||||
// that a potentially unsafe repository was found so we do not have to show
|
||||
|
|
|
@ -80,16 +80,16 @@
|
|||
],
|
||||
"file/share": [
|
||||
{
|
||||
"command": "github.copyVscodeDevLinkFile",
|
||||
"when": "github.hasGitHubRepo",
|
||||
"group": "0_vscode@0"
|
||||
"command": "github.copyVscodeDevLinkFile",
|
||||
"when": "github.hasGitHubRepo",
|
||||
"group": "0_vscode@0"
|
||||
}
|
||||
],
|
||||
"editor/context/share": [
|
||||
{
|
||||
"command": "github.copyVscodeDevLink",
|
||||
"when": "github.hasGitHubRepo && resourceScheme != untitled",
|
||||
"group": "0_vscode@0"
|
||||
"command": "github.copyVscodeDevLink",
|
||||
"when": "github.hasGitHubRepo && resourceScheme != untitled",
|
||||
"group": "0_vscode@0"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -119,12 +119,12 @@
|
|||
{
|
||||
"view": "scm",
|
||||
"contents": "%welcome.publishFolder%",
|
||||
"when": "config.git.enabled && git.state == initialized && workbenchState == folder && git.unsafeRepositoryCount == 0"
|
||||
"when": "config.git.enabled && git.state == initialized && workbenchState == folder && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0"
|
||||
},
|
||||
{
|
||||
"view": "scm",
|
||||
"contents": "%welcome.publishWorkspaceFolder%",
|
||||
"when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0 && git.unsafeRepositoryCount == 0"
|
||||
"when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0"
|
||||
}
|
||||
],
|
||||
"markdown.previewStyles": [
|
||||
|
|
Loading…
Reference in a new issue