mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-26 19:44:25 +01:00
Merge branch 'master' into pr/89249
This commit is contained in:
@@ -5,16 +5,20 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { CancellationToken, Command, Disposable, env, Event, EventEmitter, LogLevel, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, Decoration } 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 } from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status } from './api/git';
|
||||
import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery } from './api/git';
|
||||
import { AutoFetcher } from './autofetch';
|
||||
import { debounce, memoize, throttle } from './decorators';
|
||||
import { Commit, CommitOptions, ForcePushMode, GitError, Repository as BaseRepository, Stash, Submodule } from './git';
|
||||
import { Commit, ForcePushMode, 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 { IFileWatcher, watch } from './watch';
|
||||
import { Log, LogLevel } from './log';
|
||||
import { IRemoteSourceProviderRegistry } from './remoteProvider';
|
||||
import { IPushErrorHandlerRegistry } from './pushError';
|
||||
import { ApiRepository } from './api/api1';
|
||||
|
||||
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
|
||||
|
||||
@@ -39,6 +43,29 @@ export const enum ResourceGroupType {
|
||||
|
||||
export class Resource implements SourceControlResourceState {
|
||||
|
||||
static getStatusText(type: Status) {
|
||||
switch (type) {
|
||||
case Status.INDEX_MODIFIED: return localize('index modified', "Index Modified");
|
||||
case Status.MODIFIED: return localize('modified', "Modified");
|
||||
case Status.INDEX_ADDED: return localize('index added', "Index Added");
|
||||
case Status.INDEX_DELETED: return localize('index deleted', "Index Deleted");
|
||||
case Status.DELETED: return localize('deleted', "Deleted");
|
||||
case Status.INDEX_RENAMED: return localize('index renamed', "Index Renamed");
|
||||
case Status.INDEX_COPIED: return localize('index copied', "Index Copied");
|
||||
case Status.UNTRACKED: return localize('untracked', "Untracked");
|
||||
case Status.IGNORED: return localize('ignored', "Ignored");
|
||||
case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add");
|
||||
case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted");
|
||||
case Status.ADDED_BY_US: return localize('added by us', "Added By Us");
|
||||
case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them");
|
||||
case Status.ADDED_BY_THEM: return localize('added by them', "Added By Them");
|
||||
case Status.DELETED_BY_US: return localize('deleted by us', "Deleted By Us");
|
||||
case Status.BOTH_ADDED: return localize('both added', "Both Added");
|
||||
case Status.BOTH_MODIFIED: return localize('both modified', "Both Modified");
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
@memoize
|
||||
get resourceUri(): Uri {
|
||||
if (this.renameResourceUri && (this._type === Status.MODIFIED || this._type === Status.DELETED || this._type === Status.INDEX_RENAMED || this._type === Status.INDEX_COPIED)) {
|
||||
@@ -109,26 +136,7 @@ export class Resource implements SourceControlResourceState {
|
||||
}
|
||||
|
||||
private get tooltip(): string {
|
||||
switch (this.type) {
|
||||
case Status.INDEX_MODIFIED: return localize('index modified', "Index Modified");
|
||||
case Status.MODIFIED: return localize('modified', "Modified");
|
||||
case Status.INDEX_ADDED: return localize('index added', "Index Added");
|
||||
case Status.INDEX_DELETED: return localize('index deleted', "Index Deleted");
|
||||
case Status.DELETED: return localize('deleted', "Deleted");
|
||||
case Status.INDEX_RENAMED: return localize('index renamed', "Index Renamed");
|
||||
case Status.INDEX_COPIED: return localize('index copied', "Index Copied");
|
||||
case Status.UNTRACKED: return localize('untracked', "Untracked");
|
||||
case Status.IGNORED: return localize('ignored', "Ignored");
|
||||
case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add");
|
||||
case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted");
|
||||
case Status.ADDED_BY_US: return localize('added by us', "Added By Us");
|
||||
case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them");
|
||||
case Status.ADDED_BY_THEM: return localize('added by them', "Added By Them");
|
||||
case Status.DELETED_BY_US: return localize('deleted by us', "Deleted By Us");
|
||||
case Status.BOTH_ADDED: return localize('both added', "Both Added");
|
||||
case Status.BOTH_MODIFIED: return localize('both modified', "Both Modified");
|
||||
default: return '';
|
||||
}
|
||||
return Resource.getStatusText(this.type);
|
||||
}
|
||||
|
||||
private get strikeThrough(): boolean {
|
||||
@@ -197,9 +205,11 @@ export class Resource implements SourceControlResourceState {
|
||||
get color(): ThemeColor {
|
||||
switch (this.type) {
|
||||
case Status.INDEX_MODIFIED:
|
||||
return new ThemeColor('gitDecoration.stageModifiedResourceForeground');
|
||||
case Status.MODIFIED:
|
||||
return new ThemeColor('gitDecoration.modifiedResourceForeground');
|
||||
case Status.INDEX_DELETED:
|
||||
return new ThemeColor('gitDecoration.stageDeletedResourceForeground');
|
||||
case Status.DELETED:
|
||||
return new ThemeColor('gitDecoration.deletedResourceForeground');
|
||||
case Status.INDEX_ADDED:
|
||||
@@ -245,12 +255,10 @@ export class Resource implements SourceControlResourceState {
|
||||
}
|
||||
}
|
||||
|
||||
get resourceDecoration(): Decoration {
|
||||
const title = this.tooltip;
|
||||
const letter = this.letter;
|
||||
const color = this.color;
|
||||
const priority = this.priority;
|
||||
return { bubble: true, title, letter, color, priority };
|
||||
get resourceDecoration(): FileDecoration {
|
||||
const res = new FileDecoration(this.letter, this.tooltip, this.color);
|
||||
res.propagate = this.type !== Status.DELETED && this.type !== Status.INDEX_DELETED;
|
||||
return res;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -274,6 +282,7 @@ export const enum Operation {
|
||||
Clean = 'Clean',
|
||||
Branch = 'Branch',
|
||||
GetBranch = 'GetBranch',
|
||||
GetBranches = 'GetBranches',
|
||||
SetBranchUpstream = 'SetBranchUpstream',
|
||||
HashObject = 'HashObject',
|
||||
Checkout = 'Checkout',
|
||||
@@ -291,6 +300,7 @@ export const enum Operation {
|
||||
RenameBranch = 'RenameBranch',
|
||||
DeleteRef = 'DeleteRef',
|
||||
Merge = 'Merge',
|
||||
Rebase = 'Rebase',
|
||||
Ignore = 'Ignore',
|
||||
Tag = 'Tag',
|
||||
DeleteTag = 'DeleteTag',
|
||||
@@ -298,20 +308,28 @@ export const enum Operation {
|
||||
CheckIgnore = 'CheckIgnore',
|
||||
GetObjectDetails = 'GetObjectDetails',
|
||||
SubmoduleUpdate = 'SubmoduleUpdate',
|
||||
RebaseAbort = 'RebaseAbort',
|
||||
RebaseContinue = 'RebaseContinue',
|
||||
FindTrackingBranches = 'GetTracking',
|
||||
Apply = 'Apply',
|
||||
Blame = 'Blame',
|
||||
Log = 'Log',
|
||||
LogFile = 'LogFile',
|
||||
}
|
||||
|
||||
function isReadOnly(operation: Operation): boolean {
|
||||
switch (operation) {
|
||||
case Operation.Show:
|
||||
case Operation.GetCommitTemplate:
|
||||
case Operation.Blame:
|
||||
case Operation.CheckIgnore:
|
||||
case Operation.Diff:
|
||||
case Operation.FindTrackingBranches:
|
||||
case Operation.GetBranch:
|
||||
case Operation.GetCommitTemplate:
|
||||
case Operation.GetObjectDetails:
|
||||
case Operation.Log:
|
||||
case Operation.LogFile:
|
||||
case Operation.MergeBase:
|
||||
case Operation.Show:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -457,8 +475,8 @@ class FileEventLogger {
|
||||
private onDotGitFileChange: Event<Uri>,
|
||||
private outputChannel: OutputChannel
|
||||
) {
|
||||
this.logLevelDisposable = env.onDidChangeLogLevel(this.onDidChangeLogLevel, this);
|
||||
this.onDidChangeLogLevel(env.logLevel);
|
||||
this.logLevelDisposable = Log.onDidChangeLogLevel(this.onDidChangeLogLevel, this);
|
||||
this.onDidChangeLogLevel(Log.logLevel);
|
||||
}
|
||||
|
||||
private onDidChangeLogLevel(level: LogLevel): void {
|
||||
@@ -519,8 +537,8 @@ class DotGitWatcher implements IFileWatcher {
|
||||
this.transientDisposables.push(upstreamWatcher);
|
||||
upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables);
|
||||
} catch (err) {
|
||||
if (env.logLevel <= LogLevel.Error) {
|
||||
this.outputChannel.appendLine(`Failed to watch ref '${upstreamPath}', is most likely packed.\n${err.stack || err}`);
|
||||
if (Log.logLevel <= LogLevel.Error) {
|
||||
this.outputChannel.appendLine(`Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -625,6 +643,7 @@ export class Repository implements Disposable {
|
||||
}
|
||||
|
||||
this._rebaseCommit = rebaseCommit;
|
||||
commands.executeCommand('setContext', 'gitRebaseInProgress', !!this._rebaseCommit);
|
||||
}
|
||||
|
||||
get rebaseCommit(): Commit | undefined {
|
||||
@@ -665,6 +684,8 @@ export class Repository implements Disposable {
|
||||
|
||||
constructor(
|
||||
private readonly repository: BaseRepository,
|
||||
remoteSourceProviderRegistry: IRemoteSourceProviderRegistry,
|
||||
private pushErrorHandlerRegistry: IPushErrorHandlerRegistry,
|
||||
globalState: Memento,
|
||||
outputChannel: OutputChannel
|
||||
) {
|
||||
@@ -682,7 +703,7 @@ export class Repository implements Disposable {
|
||||
onDotGitFileChange = dotGitFileWatcher.event;
|
||||
this.disposables.push(dotGitFileWatcher);
|
||||
} catch (err) {
|
||||
if (env.logLevel <= LogLevel.Error) {
|
||||
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}`);
|
||||
}
|
||||
|
||||
@@ -711,10 +732,10 @@ export class Repository implements Disposable {
|
||||
this.updateInputBoxPlaceholder();
|
||||
this.disposables.push(this.onDidRunGitStatus(() => this.updateInputBoxPlaceholder()));
|
||||
|
||||
this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES"));
|
||||
this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES"));
|
||||
this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES"));
|
||||
this._untrackedGroup = this._sourceControl.createResourceGroup('untracked', localize('untracked changes', "UNTRACKED CHANGES"));
|
||||
this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "Merge Changes"));
|
||||
this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "Staged Changes"));
|
||||
this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "Changes"));
|
||||
this._untrackedGroup = this._sourceControl.createResourceGroup('untracked', localize('untracked changes', "Untracked Changes"));
|
||||
|
||||
const updateIndexGroupVisibility = () => {
|
||||
const config = workspace.getConfiguration('git', root);
|
||||
@@ -725,11 +746,20 @@ export class Repository implements Disposable {
|
||||
onConfigListener(updateIndexGroupVisibility, this, this.disposables);
|
||||
updateIndexGroupVisibility();
|
||||
|
||||
const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchSortOrder', root));
|
||||
onConfigListenerForBranchSortOrder(this.updateModelState, this, this.disposables);
|
||||
filterEvent(workspace.onDidChangeConfiguration, e =>
|
||||
e.affectsConfiguration('git.branchSortOrder', root)
|
||||
|| e.affectsConfiguration('git.untrackedChanges', root)
|
||||
|| e.affectsConfiguration('git.ignoreSubmodules', root)
|
||||
)(this.updateModelState, this, this.disposables);
|
||||
|
||||
const onConfigListenerForUntracked = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.untrackedChanges', root));
|
||||
onConfigListenerForUntracked(this.updateModelState, this, this.disposables);
|
||||
const updateInputBoxVisibility = () => {
|
||||
const config = workspace.getConfiguration('git', root);
|
||||
this._sourceControl.inputBox.visible = config.get<boolean>('showCommitInput', true);
|
||||
};
|
||||
|
||||
const onConfigListenerForInputBoxVisibility = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.showCommitInput', root));
|
||||
onConfigListenerForInputBoxVisibility(updateInputBoxVisibility, this, this.disposables);
|
||||
updateInputBoxVisibility();
|
||||
|
||||
this.mergeGroup.hideWhenEmpty = true;
|
||||
this.untrackedGroup.hideWhenEmpty = true;
|
||||
@@ -741,7 +771,7 @@ export class Repository implements Disposable {
|
||||
|
||||
this.disposables.push(new AutoFetcher(this, globalState));
|
||||
|
||||
// https://github.com/Microsoft/vscode/issues/39039
|
||||
// https://github.com/microsoft/vscode/issues/39039
|
||||
const onSuccessfulPush = filterEvent(this.onDidRunOperation, e => e.operation === Operation.Push && !e.error);
|
||||
onSuccessfulPush(() => {
|
||||
const gitConfig = workspace.getConfiguration('git');
|
||||
@@ -751,7 +781,7 @@ export class Repository implements Disposable {
|
||||
}
|
||||
}, null, this.disposables);
|
||||
|
||||
const statusBar = new StatusBarCommands(this);
|
||||
const statusBar = new StatusBarCommands(this, remoteSourceProviderRegistry);
|
||||
this.disposables.push(statusBar);
|
||||
statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables);
|
||||
this._sourceControl.statusBarCommands = statusBar.commands;
|
||||
@@ -834,14 +864,20 @@ export class Repository implements Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
const path = uri.path;
|
||||
|
||||
if (this.mergeGroup.resourceStates.some(r => r.resourceUri.path === path)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return toGitUri(uri, '', { replaceFileExtension: true });
|
||||
}
|
||||
|
||||
async getInputTemplate(): Promise<string> {
|
||||
const mergeMessage = await this.repository.getMergeMessage();
|
||||
const commitMessage = (await Promise.all([this.repository.getMergeMessage(), this.repository.getSquashMessage()])).find(msg => !!msg);
|
||||
|
||||
if (mergeMessage) {
|
||||
return mergeMessage;
|
||||
if (commitMessage) {
|
||||
return commitMessage;
|
||||
}
|
||||
|
||||
return await this.repository.getCommitTemplate();
|
||||
@@ -867,6 +903,11 @@ export class Repository implements Disposable {
|
||||
return this.run(Operation.Log, () => this.repository.log(options));
|
||||
}
|
||||
|
||||
logFile(uri: Uri, options?: LogFileOptions): Promise<Commit[]> {
|
||||
// TODO: This probably needs per-uri granularity
|
||||
return this.run(Operation.LogFile, () => this.repository.logFile(uri, options));
|
||||
}
|
||||
|
||||
@throttle
|
||||
async status(): Promise<void> {
|
||||
await this.run(Operation.Status);
|
||||
@@ -1022,6 +1063,10 @@ export class Repository implements Disposable {
|
||||
return await this.run(Operation.GetBranch, () => this.repository.getBranch(name));
|
||||
}
|
||||
|
||||
async getBranches(query: BranchQuery): Promise<Ref[]> {
|
||||
return await this.run(Operation.GetBranches, () => this.repository.getBranches(query));
|
||||
}
|
||||
|
||||
async setBranchUpstream(name: string, upstream: string): Promise<void> {
|
||||
await this.run(Operation.SetBranchUpstream, () => this.repository.setBranchUpstream(name, upstream));
|
||||
}
|
||||
@@ -1030,6 +1075,10 @@ export class Repository implements Disposable {
|
||||
await this.run(Operation.Merge, () => this.repository.merge(ref));
|
||||
}
|
||||
|
||||
async rebase(branch: string): Promise<void> {
|
||||
await this.run(Operation.Rebase, () => this.repository.rebase(branch));
|
||||
}
|
||||
|
||||
async tag(name: string, message?: string): Promise<void> {
|
||||
await this.run(Operation.Tag, () => this.repository.tag(name, message));
|
||||
}
|
||||
@@ -1070,6 +1119,10 @@ export class Repository implements Disposable {
|
||||
await this.run(Operation.Remote, () => this.repository.removeRemote(name));
|
||||
}
|
||||
|
||||
async renameRemote(name: string, newName: string): Promise<void> {
|
||||
await this.run(Operation.Remote, () => this.repository.renameRemote(name, newName));
|
||||
}
|
||||
|
||||
@throttle
|
||||
async fetchDefault(options: { silent?: boolean } = {}): Promise<void> {
|
||||
await this.fetchFrom({ silent: options.silent });
|
||||
@@ -1154,15 +1207,15 @@ export class Repository implements Disposable {
|
||||
branch = `${head.name}:${head.upstream.name}`;
|
||||
}
|
||||
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, branch, undefined, undefined, forcePushMode));
|
||||
await this.run(Operation.Push, () => this._push(remote, branch, undefined, undefined, forcePushMode));
|
||||
}
|
||||
|
||||
async pushTo(remote?: string, name?: string, setUpstream: boolean = false, forcePushMode?: ForcePushMode): Promise<void> {
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, name, setUpstream, undefined, forcePushMode));
|
||||
await this.run(Operation.Push, () => this._push(remote, name, setUpstream, undefined, forcePushMode));
|
||||
}
|
||||
|
||||
async pushFollowTags(remote?: string, forcePushMode?: ForcePushMode): Promise<void> {
|
||||
await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode));
|
||||
await this.run(Operation.Push, () => this._push(remote, undefined, false, true, forcePushMode));
|
||||
}
|
||||
|
||||
async blame(path: string): Promise<string> {
|
||||
@@ -1222,7 +1275,7 @@ export class Repository implements Disposable {
|
||||
const shouldPush = this.HEAD && (typeof this.HEAD.ahead === 'number' ? this.HEAD.ahead > 0 : true);
|
||||
|
||||
if (shouldPush) {
|
||||
await this.repository.push(remoteName, pushBranch);
|
||||
await this._push(remoteName, pushBranch);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1314,6 +1367,10 @@ export class Repository implements Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
async rebaseAbort(): Promise<void> {
|
||||
await this.run(Operation.RebaseAbort, async () => await this.repository.rebaseAbort());
|
||||
}
|
||||
|
||||
checkIgnore(filePaths: string[]): Promise<Set<string>> {
|
||||
return this.run(Operation.CheckIgnore, () => {
|
||||
return new Promise<Set<string>>((resolve, reject) => {
|
||||
@@ -1380,6 +1437,31 @@ export class Repository implements Disposable {
|
||||
return ignored;
|
||||
}
|
||||
|
||||
private async _push(remote?: string, refspec?: string, setUpstream: boolean = false, tags = false, forcePushMode?: ForcePushMode): Promise<void> {
|
||||
try {
|
||||
await this.repository.push(remote, refspec, setUpstream, tags, forcePushMode);
|
||||
} catch (err) {
|
||||
if (!remote || !refspec) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const repository = new ApiRepository(this);
|
||||
const remoteObj = repository.state.remotes.find(r => r.name === remote);
|
||||
|
||||
if (!remoteObj) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
for (const handler of this.pushErrorHandlerRegistry.getPushErrorHandlers()) {
|
||||
if (await handler.handlePushError(repository, remoteObj, refspec, err)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
private async run<T>(operation: Operation, runOperation: () => Promise<T> = () => Promise.resolve<any>(null)): Promise<T> {
|
||||
if (this.state !== RepositoryState.Idle) {
|
||||
throw new Error('Repository not initialized');
|
||||
@@ -1455,9 +1537,12 @@ export class Repository implements Disposable {
|
||||
|
||||
@throttle
|
||||
private async updateModelState(): Promise<void> {
|
||||
const { status, didHitLimit } = await this.repository.getStatus();
|
||||
const config = workspace.getConfiguration('git');
|
||||
const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
|
||||
const ignoreSubmodules = scopedConfig.get<boolean>('ignoreSubmodules');
|
||||
|
||||
const { status, didHitLimit } = await this.repository.getStatus({ ignoreSubmodules });
|
||||
|
||||
const config = workspace.getConfiguration('git');
|
||||
const shouldIgnore = config.get<boolean>('ignoreLimitWarning') === true;
|
||||
const useIcons = !config.get<boolean>('decorations.enabled', true);
|
||||
this.isRepositoryHuge = didHitLimit;
|
||||
@@ -1729,6 +1814,28 @@ export class Repository implements Disposable {
|
||||
return `${this.HEAD.behind}↓ ${this.HEAD.ahead}↑`;
|
||||
}
|
||||
|
||||
get syncTooltip(): string {
|
||||
if (!this.HEAD
|
||||
|| !this.HEAD.name
|
||||
|| !this.HEAD.commit
|
||||
|| !this.HEAD.upstream
|
||||
|| !(this.HEAD.ahead || this.HEAD.behind)
|
||||
) {
|
||||
return localize('sync changes', "Synchronize Changes");
|
||||
}
|
||||
|
||||
const remoteName = this.HEAD && this.HEAD.remote || this.HEAD.upstream.remote;
|
||||
const remote = this.remotes.find(r => r.name === remoteName);
|
||||
|
||||
if ((remote && remote.isReadOnly) || !this.HEAD.ahead) {
|
||||
return localize('pull n', "Pull {0} commits from {1}/{2}", this.HEAD.behind, this.HEAD.upstream.remote, this.HEAD.upstream.name);
|
||||
} else if (!this.HEAD.behind) {
|
||||
return localize('push n', "Push {0} commits to {1}/{2}", this.HEAD.ahead, this.HEAD.upstream.remote, this.HEAD.upstream.name);
|
||||
} else {
|
||||
return localize('pull push n', "Pull {0} and push {1} commits between {2}/{3}", this.HEAD.behind, this.HEAD.ahead, this.HEAD.upstream.remote, this.HEAD.upstream.name);
|
||||
}
|
||||
}
|
||||
|
||||
private updateInputBoxPlaceholder(): void {
|
||||
const branchName = this.headShortName;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user