mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-20 00:04:14 +08:00
Initial implementation (#240264)
This commit is contained in:
parent
732d06fd55
commit
22c92fc911
@ -3297,6 +3297,33 @@
|
||||
"maximum": 40,
|
||||
"markdownDescription": "%config.commitShortHashLength%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.diagnosticsCommitHook.Severity": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"error",
|
||||
"warning",
|
||||
"information",
|
||||
"hint"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%config.diagnosticsCommitHook.Severity.error%",
|
||||
"%config.diagnosticsCommitHook.Severity.warning%",
|
||||
"%config.diagnosticsCommitHook.Severity.information%",
|
||||
"%config.diagnosticsCommitHook.Severity.hint%"
|
||||
],
|
||||
"default": "error",
|
||||
"markdownDescription": "%config.diagnosticsCommitHook.Severity%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"git.diagnosticsCommitHook.Source": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": [],
|
||||
"markdownDescription": "%config.diagnosticsCommitHook.Source%",
|
||||
"scope": "resource"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -286,6 +286,12 @@
|
||||
"config.blameStatusBarItem.enabled": "Controls whether to show blame information in the status bar.",
|
||||
"config.blameStatusBarItem.template": "Template for the blame information status bar item. Supported variables:\n\n* `hash`: Commit hash\n\n* `hashShort`: First N characters of the commit hash according to `#git.commitShortHashLength#`\n\n* `subject`: First line of the commit message\n\n* `authorName`: Author name\n\n* `authorEmail`: Author email\n\n* `authorDate`: Author date\n\n* `authorDateAgo`: Time difference between now and the author date\n\n",
|
||||
"config.commitShortHashLength": "Controls the length of the commit short hash.",
|
||||
"config.diagnosticsCommitHook.Severity": "Controls the minimum diagnostics severity for which Git should check before committing.",
|
||||
"config.diagnosticsCommitHook.Severity.error": "Errors only",
|
||||
"config.diagnosticsCommitHook.Severity.warning": "Errors and warnings",
|
||||
"config.diagnosticsCommitHook.Severity.information": "Errors, warnings, and information",
|
||||
"config.diagnosticsCommitHook.Severity.hint": "Errors, warnings, information, and hints",
|
||||
"config.diagnosticsCommitHook.Source": "Controls the list of diagnostics sources for which Git should check before committing.",
|
||||
"submenu.explorer": "Git",
|
||||
"submenu.commit": "Commit",
|
||||
"submenu.commit.amend": "Amend",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation } from 'vscode';
|
||||
import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation, languages } from 'vscode';
|
||||
import TelemetryReporter from '@vscode/extension-telemetry';
|
||||
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
|
||||
import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote } from './api/git';
|
||||
@ -14,7 +14,7 @@ import { Model } from './model';
|
||||
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
|
||||
import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges } from './staging';
|
||||
import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri';
|
||||
import { dispose, getCommitShortHash, grep, isDefined, isDescendant, pathEquals, relativePath, truncate } from './util';
|
||||
import { DiagnosticSeverityConfig, dispose, getCommitShortHash, grep, isDefined, isDescendant, pathEquals, relativePath, toDiagnosticSeverity, truncate } from './util';
|
||||
import { GitTimelineItem } from './timelineProvider';
|
||||
import { ApiRepository } from './api/api1';
|
||||
import { getRemoteSourceActions, pickRemoteSource } from './remoteSource';
|
||||
@ -617,6 +617,66 @@ class CommandErrorOutputTextDocumentContentProvider implements TextDocumentConte
|
||||
}
|
||||
}
|
||||
|
||||
async function evaluateDiagnosticsCommitHook(repository: Repository, options: CommitOptions): Promise<boolean> {
|
||||
const config = workspace.getConfiguration('git', Uri.file(repository.root));
|
||||
const diagnosticSource = config.get<string[]>('diagnosticsCommitHook.Source', []);
|
||||
const diagnosticSeveritySetting = config.get<DiagnosticSeverityConfig>('diagnosticsCommitHook.Severity', 'error');
|
||||
const diagnosticSeverity = toDiagnosticSeverity(diagnosticSeveritySetting);
|
||||
|
||||
if (diagnosticSource.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const changes: Uri[] = [];
|
||||
if (repository.indexGroup.resourceStates.length > 0) {
|
||||
// Staged files
|
||||
changes.push(...repository.indexGroup.resourceStates.map(r => r.resourceUri));
|
||||
} else if (options.all === 'tracked') {
|
||||
// Tracked files
|
||||
changes.push(...repository.workingTreeGroup.resourceStates
|
||||
.filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED)
|
||||
.map(r => r.resourceUri));
|
||||
} else {
|
||||
// All files
|
||||
changes.push(...repository.workingTreeGroup.resourceStates.map(r => r.resourceUri));
|
||||
changes.push(...repository.untrackedGroup.resourceStates.map(r => r.resourceUri));
|
||||
}
|
||||
|
||||
const diagnostics = languages.getDiagnostics();
|
||||
const changesDiagnostics = diagnostics.filter(([uri, diags]) =>
|
||||
// File
|
||||
changes.some(u => uri.scheme === 'file' && pathEquals(u.fsPath, uri.fsPath)) &&
|
||||
// Severity
|
||||
diags.some(d => d.source && diagnosticSource.includes(d.source) && d.severity <= diagnosticSeverity)
|
||||
);
|
||||
|
||||
if (changesDiagnostics.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show dialog
|
||||
const commit = l10n.t('Commit Anyway');
|
||||
const view = l10n.t('View Problems');
|
||||
|
||||
const message = changesDiagnostics.length === 1
|
||||
? l10n.t('The following file has unresolved diagnostic information: {0}.\n\nHow would you like to proceed?', path.basename(changesDiagnostics[0][0].fsPath))
|
||||
: l10n.t('There are {0} files that have unresolved diagnostic information.\n\nHow would you like to proceed?', changesDiagnostics.length);
|
||||
|
||||
const choice = await window.showWarningMessage(message, { modal: true }, commit, view);
|
||||
|
||||
// Commit Anyway
|
||||
if (choice === commit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// View Problems
|
||||
if (choice === view) {
|
||||
commands.executeCommand('workbench.panel.markers.view.focus');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export class CommandCenter {
|
||||
|
||||
private disposables: Disposable[];
|
||||
@ -2273,7 +2333,13 @@ export class CommandCenter {
|
||||
opts.all = 'tracked';
|
||||
}
|
||||
|
||||
// Branch protection
|
||||
// Diagnostics commit hook
|
||||
const diagnosticsResult = await evaluateDiagnosticsCommitHook(repository, opts);
|
||||
if (!diagnosticsResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Branch protection commit hook
|
||||
const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!;
|
||||
if (repository.isBranchProtected() && (branchProtectionPrompt === 'alwaysPrompt' || branchProtectionPrompt === 'alwaysCommitToNewBranch')) {
|
||||
const commitToNewBranch = l10n.t('Commit to a New Branch');
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef, l10n, workspace, Uri } from 'vscode';
|
||||
import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef, l10n, workspace, Uri, DiagnosticSeverity } from 'vscode';
|
||||
import { dirname, sep, relative } from 'path';
|
||||
import { Readable } from 'stream';
|
||||
import { promises as fs, createReadStream } from 'fs';
|
||||
@ -772,3 +772,15 @@ export function getCommitShortHash(scope: Uri, hash: string): string {
|
||||
const shortHashLength = config.get<number>('commitShortHashLength', 7);
|
||||
return hash.substring(0, shortHashLength);
|
||||
}
|
||||
|
||||
export type DiagnosticSeverityConfig = 'error' | 'warning' | 'information' | 'hint';
|
||||
|
||||
export function toDiagnosticSeverity(value: DiagnosticSeverityConfig): DiagnosticSeverity {
|
||||
return value === 'error'
|
||||
? DiagnosticSeverity.Error
|
||||
: value === 'warning'
|
||||
? DiagnosticSeverity.Warning
|
||||
: value === 'information'
|
||||
? DiagnosticSeverity.Information
|
||||
: DiagnosticSeverity.Hint;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user