Merge from vscode merge-base (#22780)

* Revert "Revert "Merge from vscode merge-base (#22769)" (#22779)"

This reverts commit 47a1745180.

* Fix notebook download task

* Remove done call from extensions-ci
This commit is contained in:
Karl Burtram
2023-04-19 21:48:46 -07:00
committed by GitHub
parent decbe8dded
commit e7d3d047ec
2389 changed files with 92155 additions and 42602 deletions

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, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode';
import * as picomatch from 'picomatch';
import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, 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';
@@ -14,13 +15,14 @@ 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, logTimestamp, onceEvent, pathEquals, relativePath } from './util';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util';
import { IFileWatcher, watch } from './watch';
import { Log, LogLevel } from './log';
import { LogLevel, OutputChannelLogger } from './log';
import { IPushErrorHandlerRegistry } from './pushError';
import { ApiRepository } from './api/api1';
import { IRemoteSourcePublisherRegistry } from './remotePublisher';
import { ActionButtonCommand } from './actionButton';
import { IPostCommitCommandsProviderRegistry } from './postCommitCommands';
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
@@ -296,6 +298,10 @@ export class Resource implements SourceControlResourceState {
const command = this._commandResolver.resolveChangeCommand(this);
await commands.executeCommand<void>(command.command, ...(command.arguments || []));
}
clone() {
return new Resource(this._commandResolver, this._resourceGroupType, this._resourceUri, this._type, this._useIcons, this._renameResourceUri);
}
}
export const enum Operation {
@@ -450,6 +456,13 @@ class ProgressManager {
const onDidChange = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git', Uri.file(this.repository.root)));
onDidChange(_ => this.updateEnablement());
this.updateEnablement();
this.repository.onDidChangeOperations(() => {
const commitInProgress = this.repository.operations.isRunning(Operation.Commit);
this.repository.sourceControl.inputBox.enabled = !commitInProgress;
commands.executeCommand('setContext', 'commitInProgress', commitInProgress);
});
}
private updateEnablement(): void {
@@ -504,10 +517,10 @@ class FileEventLogger {
constructor(
private onWorkspaceWorkingTreeFileChange: Event<Uri>,
private onDotGitFileChange: Event<Uri>,
private outputChannel: OutputChannel
private outputChannelLogger: OutputChannelLogger
) {
this.logLevelDisposable = Log.onDidChangeLogLevel(this.onDidChangeLogLevel, this);
this.onDidChangeLogLevel(Log.logLevel);
this.logLevelDisposable = outputChannelLogger.onDidChangeLogLevel(this.onDidChangeLogLevel, this);
this.onDidChangeLogLevel(outputChannelLogger.currentLogLevel);
}
private onDidChangeLogLevel(level: LogLevel): void {
@@ -518,8 +531,8 @@ class FileEventLogger {
}
this.eventDisposable = combinedDisposable([
this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [wt] Change: ${uri.fsPath}`)),
this.onDotGitFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [.git] Change: ${uri.fsPath}`))
this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannelLogger.logDebug(`[wt] Change: ${uri.fsPath}`)),
this.onDotGitFileChange(uri => this.outputChannelLogger.logDebug(`[.git] Change: ${uri.fsPath}`))
]);
}
@@ -539,14 +552,14 @@ class DotGitWatcher implements IFileWatcher {
constructor(
private repository: Repository,
private outputChannel: OutputChannel
private outputChannelLogger: OutputChannelLogger
) {
const rootWatcher = watch(repository.dotGit.path);
this.disposables.push(rootWatcher);
// 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));
const filteredRootWatcher = filterEvent(rootWatcher.event, uri => uri.scheme === 'file' && !/\/\.git(\/index\.lock)?$|\/\.watchman-cookie-/.test(uri.path));
this.event = anyEvent(filteredRootWatcher, this.emitter.event);
repository.onDidRunGitStatus(this.updateTransientWatchers, this, this.disposables);
@@ -570,9 +583,7 @@ class DotGitWatcher implements IFileWatcher {
this.transientDisposables.push(upstreamWatcher);
upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
} catch (err) {
if (Log.logLevel <= LogLevel.Error) {
this.outputChannel.appendLine(`${logTimestamp()} Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`);
}
this.outputChannelLogger.logWarning(`Failed to watch ref '${upstreamPath}', is most likely packed.`);
}
}
@@ -605,11 +616,20 @@ class ResourceCommandResolver {
const title = this.getTitle(resource);
if (!resource.leftUri) {
return {
command: 'vscode.open',
title: localize('open', "Open"),
arguments: [resource.rightUri, { override: resource.type === Status.BOTH_MODIFIED ? false : undefined }, title]
};
const bothModified = resource.type === Status.BOTH_MODIFIED;
if (resource.rightUri && workspace.getConfiguration('git').get<boolean>('mergeEditor', false) && (bothModified || resource.type === Status.BOTH_ADDED)) {
return {
command: '_git.openMergeEditor',
title: localize('open.merge', "Open Merge"),
arguments: [resource.rightUri]
};
} else {
return {
command: 'vscode.open',
title: localize('open', "Open"),
arguments: [resource.rightUri, { override: bothModified ? false : undefined }, title]
};
}
} else {
return {
command: 'vscode.diff',
@@ -849,6 +869,7 @@ export class Repository implements Disposable {
private isRepositoryHuge: false | { limit: number } = false;
private didWarnAboutLimit = false;
private isBranchProtectedMatcher: picomatch.Matcher | undefined;
private resourceCommandResolver = new ResourceCommandResolver(this);
private disposables: Disposable[] = [];
@@ -856,8 +877,9 @@ export class Repository implements Disposable {
private readonly repository: BaseRepository,
private pushErrorHandlerRegistry: IPushErrorHandlerRegistry,
remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry,
postCommitCommandsProviderRegistry: IPostCommitCommandsProviderRegistry,
globalState: Memento,
outputChannel: OutputChannel,
outputChannelLogger: OutputChannelLogger,
private telemetryReporter: TelemetryReporter
) {
const repositoryWatcher = workspace.createFileSystemWatcher(new RelativePattern(Uri.file(repository.root), '**'));
@@ -869,13 +891,11 @@ export class Repository implements Disposable {
let onRepositoryDotGitFileChange: Event<Uri>;
try {
const dotGitFileWatcher = new DotGitWatcher(this, outputChannel);
const dotGitFileWatcher = new DotGitWatcher(this, outputChannelLogger);
onRepositoryDotGitFileChange = dotGitFileWatcher.event;
this.disposables.push(dotGitFileWatcher);
} catch (err) {
if (Log.logLevel <= LogLevel.Error) {
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}`);
}
outputChannelLogger.logError(`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}`);
onRepositoryDotGitFileChange = filterEvent(onRepositoryFileChange, uri => /\.git($|\/)/.test(uri.path));
}
@@ -889,7 +909,7 @@ export class Repository implements Disposable {
// Relevate repository changes should trigger virtual document change events
onRepositoryDotGitFileChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, this.disposables);
this.disposables.push(new FileEventLogger(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange, outputChannel));
this.disposables.push(new FileEventLogger(onRepositoryWorkingTreeFileChange, onRepositoryDotGitFileChange, outputChannelLogger));
const root = Uri.file(repository.root);
this._sourceControl = scm.createSourceControl('git', 'Git', root);
@@ -916,13 +936,19 @@ export class Repository implements Disposable {
onConfigListener(updateIndexGroupVisibility, this, this.disposables);
updateIndexGroupVisibility();
workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('git.mergeEditor')) {
this.mergeGroup.resourceStates = this.mergeGroup.resourceStates.map(r => r.clone());
}
}, undefined, this.disposables);
filterEvent(workspace.onDidChangeConfiguration, e =>
e.affectsConfiguration('git.branchSortOrder', root)
e.affectsConfiguration('git.branchProtection', root)
|| e.affectsConfiguration('git.branchSortOrder', root)
|| e.affectsConfiguration('git.untrackedChanges', root)
|| e.affectsConfiguration('git.ignoreSubmodules', root)
|| e.affectsConfiguration('git.openDiffOnClick', root)
|| e.affectsConfiguration('git.rebaseWhenSync', root)
|| e.affectsConfiguration('git.showUnpublishedCommitsButton', root)
|| e.affectsConfiguration('git.showActionButton', root)
)(this.updateModelState, this, this.disposables);
const updateInputBoxVisibility = () => {
@@ -963,12 +989,16 @@ export class Repository implements Disposable {
}
}, null, this.disposables);
const onDidChangeBranchProtection = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchProtection', root));
onDidChangeBranchProtection(this.updateBranchProtectionMatcher, this, this.disposables);
this.updateBranchProtectionMatcher();
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);
const actionButton = new ActionButtonCommand(this, postCommitCommandsProviderRegistry);
this.disposables.push(actionButton);
actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button);
this._sourceControl.actionButton = actionButton.button;
@@ -1014,7 +1044,7 @@ export class Repository implements Disposable {
}
let lineNumber = 0;
let start = 0, end;
let start = 0;
let match: RegExpExecArray | null;
const regex = /\r?\n/g;
@@ -1023,7 +1053,7 @@ export class Repository implements Disposable {
lineNumber++;
}
end = match ? match.index : text.length;
const end = match ? match.index : text.length;
const line = text.substring(start, end);
@@ -1267,7 +1297,7 @@ export class Repository implements Disposable {
});
}
closeDiffEditors(indexResources: string[], workingTreeResources: string[], ignoreSetting: boolean = false): void {
closeDiffEditors(indexResources: string[] | undefined, workingTreeResources: string[] | undefined, ignoreSetting: boolean = false): void {
const config = workspace.getConfiguration('git', Uri.file(this.root));
if (!config.get<boolean>('closeDiffOnOperation', false) && !ignoreSetting) { return; }
@@ -1276,11 +1306,11 @@ export class Repository implements Disposable {
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))) {
if (input.modified.scheme === 'git' && (indexResources === undefined || 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))) {
if (input.modified.scheme === 'file' && input.original.scheme === 'git' && (workingTreeResources === undefined || workingTreeResources.some(r => pathEquals(r, input.modified.fsPath)))) {
// Working Tree
diffEditorTabsToClose.push(tab);
}
@@ -1386,15 +1416,15 @@ export class Repository implements Disposable {
}
@throttle
async fetchAll(): Promise<void> {
await this._fetch({ all: true });
async fetchAll(cancellationToken?: CancellationToken): Promise<void> {
await this._fetch({ all: true, cancellationToken });
}
async fetch(options: FetchOptions): Promise<void> {
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; cancellationToken?: CancellationToken } = {}): Promise<void> {
if (!options.prune) {
const config = workspace.getConfiguration('git', Uri.file(this.root));
const prune = config.get<boolean>('pruneOnFetch');
@@ -1439,7 +1469,7 @@ export class Repository implements Disposable {
// When fetchOnPull is enabled, fetch all branches when pulling
if (fetchOnPull) {
await this.repository.fetch({ all: true });
await this.fetchAll();
}
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
@@ -1479,13 +1509,8 @@ export class Repository implements Disposable {
}
@throttle
sync(head: Branch): Promise<void> {
return this._sync(head, false);
}
@throttle
async syncRebase(head: Branch): Promise<void> {
return this._sync(head, true);
sync(head: Branch, rebase: boolean): Promise<void> {
return this._sync(head, rebase);
}
private async _sync(head: Branch, rebase: boolean): Promise<void> {
@@ -1510,7 +1535,7 @@ export class Repository implements Disposable {
const fn = async (cancellationToken?: CancellationToken) => {
// When fetchOnPull is enabled, fetch all branches when pulling
if (fetchOnPull) {
await this.repository.fetch({ all: true, cancellationToken });
await this.fetchAll(cancellationToken);
}
if (await this.checkIfMaybeRebased(this.HEAD?.name)) {
@@ -1868,9 +1893,10 @@ export class Repository implements Disposable {
if (didHitLimit) {
/* __GDPR__
"statusLimit" : {
"ignoreSubmodules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"limit": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"statusLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
"owner": "lszomoru",
"ignoreSubmodules": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Setting indicating whether submodules are ignored" },
"limit": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Setting indicating the limit of status entries" },
"statusLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Total number of status entries" }
}
*/
this.telemetryReporter.sendTelemetryEvent('statusLimit', { ignoreSubmodules: String(ignoreSubmodules) }, { limit, statusLength });
@@ -2002,6 +2028,9 @@ export class Repository implements Disposable {
// set count badge
this.setCountBadge();
// set mergeChanges context
commands.executeCommand('setContext', 'git.mergeChanges', merge.map(item => item.resourceUri.toString()));
this._onDidChangeStatus.fire();
this._sourceControl.commitTemplate = await this.getInputTemplate();
@@ -2188,6 +2217,21 @@ export class Repository implements Disposable {
}
}
private updateBranchProtectionMatcher(): void {
const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
const branchProtectionGlobs = scopedConfig.get<string[]>('branchProtection')!.map(bp => bp.trim()).filter(bp => bp !== '');
if (branchProtectionGlobs.length === 0) {
this.isBranchProtectedMatcher = undefined;
} else {
this.isBranchProtectedMatcher = picomatch(branchProtectionGlobs);
}
}
public isBranchProtected(name: string = this.HEAD?.name ?? ''): boolean {
return this.isBranchProtectedMatcher ? this.isBranchProtectedMatcher(name) : false;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}