未验证 提交 7563aa3c 编写于 作者: J Joao Moreno

Merge branch 'pr/80083'

...@@ -109,6 +109,24 @@ ...@@ -109,6 +109,24 @@
"dark": "resources/icons/dark/stage.svg" "dark": "resources/icons/dark/stage.svg"
} }
}, },
{
"command": "git.stageAllTracked",
"title": "%command.stageAllTracked%",
"category": "Git",
"icon": {
"light": "resources/icons/light/stage.svg",
"dark": "resources/icons/dark/stage.svg"
}
},
{
"command": "git.stageAllUntracked",
"title": "%command.stageAllUntracked%",
"category": "Git",
"icon": {
"light": "resources/icons/light/stage.svg",
"dark": "resources/icons/dark/stage.svg"
}
},
{ {
"command": "git.stageSelectedRanges", "command": "git.stageSelectedRanges",
"title": "%command.stageSelectedRanges%", "title": "%command.stageSelectedRanges%",
...@@ -178,6 +196,24 @@ ...@@ -178,6 +196,24 @@
"dark": "resources/icons/dark/clean.svg" "dark": "resources/icons/dark/clean.svg"
} }
}, },
{
"command": "git.cleanAllTracked",
"title": "%command.cleanAllTracked%",
"category": "Git",
"icon": {
"light": "resources/icons/light/clean.svg",
"dark": "resources/icons/dark/clean.svg"
}
},
{
"command": "git.cleanAllUntracked",
"title": "%command.cleanAllUntracked%",
"category": "Git",
"icon": {
"light": "resources/icons/light/clean.svg",
"dark": "resources/icons/dark/clean.svg"
}
},
{ {
"command": "git.commit", "command": "git.commit",
"title": "%command.commit%", "title": "%command.commit%",
...@@ -450,6 +486,14 @@ ...@@ -450,6 +486,14 @@
"command": "git.stageAll", "command": "git.stageAll",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && gitOpenRepositoryCount != 0"
}, },
{
"command": "git.stageAllTracked",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{
"command": "git.stageAllUntracked",
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
},
{ {
"command": "git.stageSelectedRanges", "command": "git.stageSelectedRanges",
"when": "config.git.enabled && gitOpenRepositoryCount != 0" "when": "config.git.enabled && gitOpenRepositoryCount != 0"
...@@ -863,22 +907,62 @@ ...@@ -863,22 +907,62 @@
}, },
{ {
"command": "git.cleanAll", "command": "git.cleanAll",
"when": "scmProvider == git && scmResourceGroup == workingTree", "when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges == default",
"group": "1_modification" "group": "1_modification"
}, },
{ {
"command": "git.stageAll", "command": "git.stageAll",
"when": "scmProvider == git && scmResourceGroup == workingTree", "when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges == default",
"group": "1_modification" "group": "1_modification"
}, },
{ {
"command": "git.cleanAll", "command": "git.cleanAll",
"when": "scmProvider == git && scmResourceGroup == workingTree", "when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges == default",
"group": "inline" "group": "inline"
}, },
{ {
"command": "git.stageAll", "command": "git.stageAll",
"when": "scmProvider == git && scmResourceGroup == workingTree", "when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges == default",
"group": "inline"
},
{
"command": "git.cleanAllTracked",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges != default",
"group": "1_modification"
},
{
"command": "git.stageAllTracked",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges != default",
"group": "1_modification"
},
{
"command": "git.cleanAllTracked",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges != default",
"group": "inline"
},
{
"command": "git.stageAllTracked",
"when": "scmProvider == git && scmResourceGroup == workingTree && config.git.untrackedChanges != default",
"group": "inline"
},
{
"command": "git.cleanAllUntracked",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "1_modification"
},
{
"command": "git.stageAllUntracked",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "1_modification"
},
{
"command": "git.cleanAllUntracked",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline"
},
{
"command": "git.stageAllUntracked",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline" "group": "inline"
} }
], ],
...@@ -1054,6 +1138,61 @@ ...@@ -1054,6 +1138,61 @@
"command": "git.revealInExplorer", "command": "git.revealInExplorer",
"when": "scmProvider == git && scmResourceGroup == workingTree", "when": "scmProvider == git && scmResourceGroup == workingTree",
"group": "2_view" "group": "2_view"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == workingTree",
"group": "navigation"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "navigation"
},
{
"command": "git.openHEADFile",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "navigation"
},
{
"command": "git.openFile",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "navigation"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "1_modification"
},
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == untracked && !gitFreshRepository",
"group": "1_modification"
},
{
"command": "git.clean",
"when": "scmProvider == git && scmResourceGroup == untracked && !gitFreshRepository",
"group": "inline"
},
{
"command": "git.stage",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "inline"
},
{
"command": "git.openFile2",
"when": "scmProvider == git && scmResourceGroup == untracked && config.git.showInlineOpenFileAction && config.git.openDiffOnClick",
"group": "inline0"
},
{
"command": "git.openChange",
"when": "scmProvider == git && scmResourceGroup == untracked && config.git.showInlineOpenFileAction && !config.git.openDiffOnClick",
"group": "inline0"
},
{
"command": "git.ignore",
"when": "scmProvider == git && scmResourceGroup == untracked",
"group": "1_modification@3"
} }
], ],
"editor/title": [ "editor/title": [
...@@ -1197,7 +1336,8 @@ ...@@ -1197,7 +1336,8 @@
"%config.countBadge.off%" "%config.countBadge.off%"
], ],
"description": "%config.countBadge%", "description": "%config.countBadge%",
"default": "all" "default": "all",
"scope": "resource"
}, },
"git.checkoutType": { "git.checkoutType": {
"type": "string", "type": "string",
...@@ -1457,6 +1597,22 @@ ...@@ -1457,6 +1597,22 @@
], ],
"default": "committerdate", "default": "committerdate",
"description": "%config.branchSortOrder%" "description": "%config.branchSortOrder%"
},
"git.untrackedChanges": {
"type": "string",
"enum": [
"default",
"separate",
"hidden"
],
"enumDescriptions": [
"%config.untrackedChanges.default%",
"%config.untrackedChanges.separate%",
"%config.untrackedChanges.hidden%"
],
"default": "default",
"description": "%config.untrackedChanges%",
"scope": "resource"
} }
} }
}, },
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
"command.openHEADFile": "Open File (HEAD)", "command.openHEADFile": "Open File (HEAD)",
"command.stage": "Stage Changes", "command.stage": "Stage Changes",
"command.stageAll": "Stage All Changes", "command.stageAll": "Stage All Changes",
"command.stageAllTracked": "Stage All Tracked Changes",
"command.stageAllUntracked": "Stage All Untracked Changes",
"command.stageSelectedRanges": "Stage Selected Ranges", "command.stageSelectedRanges": "Stage Selected Ranges",
"command.revertSelectedRanges": "Revert Selected Ranges", "command.revertSelectedRanges": "Revert Selected Ranges",
"command.stageChange": "Stage Change", "command.stageChange": "Stage Change",
...@@ -20,6 +22,8 @@ ...@@ -20,6 +22,8 @@
"command.unstageSelectedRanges": "Unstage Selected Ranges", "command.unstageSelectedRanges": "Unstage Selected Ranges",
"command.clean": "Discard Changes", "command.clean": "Discard Changes",
"command.cleanAll": "Discard All Changes", "command.cleanAll": "Discard All Changes",
"command.cleanAllTracked": "Discard All Tracked Changes",
"command.cleanAllUntracked": "Discard All Untracked Changes",
"command.commit": "Commit", "command.commit": "Commit",
"command.commitStaged": "Commit Staged", "command.commitStaged": "Commit Staged",
"command.commitEmpty": "Commit Empty", "command.commitEmpty": "Commit Empty",
...@@ -131,6 +135,10 @@ ...@@ -131,6 +135,10 @@
"config.openDiffOnClick": "Controls whether the diff editor should be opened when clicking a change. Otherwise the regular editor will be opened.", "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.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.", "config.branchSortOrder": "Controls the sort order for branches.",
"config.untrackedChanges": "Controls how untracked changes behave.",
"config.untrackedChanges.default": "All changes, tracked and untracked, appear together and behave equally.",
"config.untrackedChanges.separate": "Untracked changes appear separately in the Source Control view. They are also excluded from several actions.",
"config.untrackedChanges.hidden": "Untracked changes are hidden and excluded from several actions.",
"colors.added": "Color for added resources.", "colors.added": "Color for added resources.",
"colors.modified": "Color for modified resources.", "colors.modified": "Color for modified resources.",
"colors.deleted": "Color for deleted resources.", "colors.deleted": "Color for deleted resources.",
......
...@@ -3,19 +3,19 @@ ...@@ -3,19 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Uri, commands, Disposable, window, workspace, QuickPickItem, OutputChannel, Range, WorkspaceEdit, Position, LineChange, SourceControlResourceState, TextDocumentShowOptions, ViewColumn, ProgressLocation, TextEditor, MessageOptions, WorkspaceFolder } from 'vscode';
import { Git, CommitOptions, Stash, ForcePushMode } from './git';
import { Repository, Resource, ResourceGroupType } from './repository';
import { Model } from './model';
import { toGitUri, fromGitUri } from './uri';
import { grep, isDescendant, pathEquals } from './util';
import { applyLineChanges, intersectDiffWithRange, toLineRanges, invertLineChange, getModifiedRange } from './staging';
import * as path from 'path';
import { lstat, Stats } from 'fs'; import { lstat, Stats } from 'fs';
import * as os from 'os'; import * as os from 'os';
import * as path from 'path';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry'; import TelemetryReporter from 'vscode-extension-telemetry';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import { Ref, RefType, Branch, GitErrorCodes, Status } from './api/git'; import { Branch, GitErrorCodes, Ref, RefType, Status } from './api/git';
import { CommitOptions, ForcePushMode, 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 } from './uri';
import { grep, isDescendant, pathEquals } from './util';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
...@@ -872,7 +872,8 @@ export class CommandCenter { ...@@ -872,7 +872,8 @@ export class CommandCenter {
} }
const workingTree = selection.filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree); const workingTree = selection.filter(s => s.resourceGroupType === ResourceGroupType.WorkingTree);
const scmResources = [...workingTree, ...resolved, ...unresolved]; 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(`git.stage.scmResources ${scmResources.length}`);
if (!scmResources.length) { if (!scmResources.length) {
...@@ -913,7 +914,9 @@ export class CommandCenter { ...@@ -913,7 +914,9 @@ export class CommandCenter {
} }
} }
await repository.add([]); const config = workspace.getConfiguration('git', Uri.file(repository.root));
const untrackedChanges = config.get<'default' | 'separate' | 'hidden'>('untrackedChanges');
await repository.add([], untrackedChanges === 'default' ? undefined : { update: true });
} }
private async _stageDeletionConflict(repository: Repository, uri: Uri): Promise<void> { private async _stageDeletionConflict(repository: Repository, uri: Uri): Promise<void> {
...@@ -951,6 +954,24 @@ export class CommandCenter { ...@@ -951,6 +954,24 @@ export class CommandCenter {
} }
} }
@command('git.stageAllTracked', { repository: true })
async stageAllTracked(repository: Repository): Promise<void> {
const resources = repository.workingTreeGroup.resourceStates
.filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED);
const uris = resources.map(r => r.resourceUri);
await repository.add(uris);
}
@command('git.stageAllUntracked', { repository: true })
async stageAllUntracked(repository: Repository): Promise<void> {
const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates]
.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED);
const uris = resources.map(r => r.resourceUri);
await repository.add(uris);
}
@command('git.stageChange') @command('git.stageChange')
async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> { async stageChange(uri: Uri, changes: LineChange[], index: number): Promise<void> {
const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0]; const textEditor = window.visibleTextEditors.filter(e => e.document.uri.toString() === uri.toString())[0];
...@@ -1137,8 +1158,8 @@ export class CommandCenter { ...@@ -1137,8 +1158,8 @@ export class CommandCenter {
resourceStates = [resource]; resourceStates = [resource];
} }
const scmResources = resourceStates const scmResources = resourceStates.filter(s => s instanceof Resource
.filter(s => s instanceof Resource && s.resourceGroupType === ResourceGroupType.WorkingTree) as Resource[]; && (s.resourceGroupType === ResourceGroupType.WorkingTree || s.resourceGroupType === ResourceGroupType.Untracked)) as Resource[];
if (!scmResources.length) { if (!scmResources.length) {
return; return;
...@@ -1195,41 +1216,11 @@ export class CommandCenter { ...@@ -1195,41 +1216,11 @@ export class CommandCenter {
const untrackedResources = resources.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED); const untrackedResources = resources.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED);
if (untrackedResources.length === 0) { if (untrackedResources.length === 0) {
const message = resources.length === 1 await this._cleanTrackedChanges(repository, resources);
? localize('confirm discard all single', "Are you sure you want to discard changes in {0}?", path.basename(resources[0].resourceUri.fsPath))
: localize('confirm discard all', "Are you sure you want to discard ALL changes in {0} files?\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST.", resources.length);
const yes = resources.length === 1
? localize('discardAll multiple', "Discard 1 File")
: localize('discardAll', "Discard All {0} Files", resources.length);
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
await repository.clean(resources.map(r => r.resourceUri));
return;
} else if (resources.length === 1) { } else if (resources.length === 1) {
const message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(resources[0].resourceUri.fsPath)); await this._cleanUntrackedChange(repository, resources[0]);
const yes = localize('delete file', "Delete file");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
await repository.clean(resources.map(r => r.resourceUri));
} else if (trackedResources.length === 0) { } else if (trackedResources.length === 0) {
const message = localize('confirm delete multiple', "Are you sure you want to DELETE {0} files?", resources.length); await this._cleanUntrackedChanges(repository, resources);
const yes = localize('delete files', "Delete Files");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
await repository.clean(resources.map(r => r.resourceUri));
} else { // resources.length > 1 && untrackedResources.length > 0 && trackedResources.length > 0 } else { // resources.length > 1 && untrackedResources.length > 0 && trackedResources.length > 0
const untrackedMessage = untrackedResources.length === 1 const untrackedMessage = untrackedResources.length === 1
? localize('there are untracked files single', "The following untracked file will be DELETED FROM DISK if discarded: {0}.", path.basename(untrackedResources[0].resourceUri.fsPath)) ? localize('there are untracked files single', "The following untracked file will be DELETED FROM DISK if discarded: {0}.", path.basename(untrackedResources[0].resourceUri.fsPath))
...@@ -1254,6 +1245,74 @@ export class CommandCenter { ...@@ -1254,6 +1245,74 @@ export class CommandCenter {
} }
} }
@command('git.cleanAllTracked', { repository: true })
async cleanAllTracked(repository: Repository): Promise<void> {
const resources = repository.workingTreeGroup.resourceStates
.filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED);
if (resources.length === 0) {
return;
}
await this._cleanTrackedChanges(repository, resources);
}
@command('git.cleanAllUntracked', { repository: true })
async cleanAllUntracked(repository: Repository): Promise<void> {
const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates]
.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED);
if (resources.length === 0) {
return;
}
if (resources.length === 1) {
await this._cleanUntrackedChange(repository, resources[0]);
} else {
await this._cleanUntrackedChanges(repository, resources);
}
}
private async _cleanTrackedChanges(repository: Repository, resources: Resource[]): Promise<void> {
const message = resources.length === 1
? localize('confirm discard all single', "Are you sure you want to discard changes in {0}?", path.basename(resources[0].resourceUri.fsPath))
: localize('confirm discard all', "Are you sure you want to discard ALL changes in {0} files?\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST.", resources.length);
const yes = resources.length === 1
? localize('discardAll multiple', "Discard 1 File")
: localize('discardAll', "Discard All {0} Files", resources.length);
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
await repository.clean(resources.map(r => r.resourceUri));
}
private async _cleanUntrackedChange(repository: Repository, resource: Resource): Promise<void> {
const message = localize('confirm delete', "Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST.", path.basename(resource.resourceUri.fsPath));
const yes = localize('delete file', "Delete file");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
await repository.clean([resource.resourceUri]);
}
private async _cleanUntrackedChanges(repository: Repository, resources: Resource[]): Promise<void> {
const message = localize('confirm delete multiple', "Are you sure you want to DELETE {0} files?", resources.length);
const yes = localize('delete files', "Delete Files");
const pick = await window.showWarningMessage(message, { modal: true }, yes);
if (pick !== yes) {
return;
}
await repository.clean(resources.map(r => r.resourceUri));
}
private async smartCommit( private async smartCommit(
repository: Repository, repository: Repository,
getCommitMessage: () => Promise<string | undefined>, getCommitMessage: () => Promise<string | undefined>,
...@@ -1362,6 +1421,10 @@ export class CommandCenter { ...@@ -1362,6 +1421,10 @@ export class CommandCenter {
opts.all = 'tracked'; opts.all = 'tracked';
} }
if (opts.all && config.get<'default' | 'separate' | 'hidden'>('untrackedChanges') !== 'default') {
opts.all = 'tracked';
}
await repository.commit(message, opts); await repository.commit(message, opts);
const postCommitCommand = config.get<'none' | 'push' | 'sync'>('postCommitCommand'); const postCommitCommand = config.get<'none' | 'push' | 'sync'>('postCommitCommand');
...@@ -2159,7 +2222,8 @@ export class CommandCenter { ...@@ -2159,7 +2222,8 @@ export class CommandCenter {
} }
private async _stash(repository: Repository, includeUntracked = false): Promise<void> { private async _stash(repository: Repository, includeUntracked = false): Promise<void> {
const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0; const noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0
&& (!includeUntracked || repository.untrackedGroup.resourceStates.length === 0);
const noStagedChanges = repository.indexGroup.resourceStates.length === 0; const noStagedChanges = repository.indexGroup.resourceStates.length === 0;
if (noUnstagedChanges && noStagedChanges) { if (noUnstagedChanges && noStagedChanges) {
......
...@@ -113,6 +113,7 @@ class GitDecorationProvider implements DecorationProvider { ...@@ -113,6 +113,7 @@ class GitDecorationProvider implements DecorationProvider {
this.collectSubmoduleDecorationData(newDecorations); this.collectSubmoduleDecorationData(newDecorations);
this.collectDecorationData(this.repository.indexGroup, newDecorations); this.collectDecorationData(this.repository.indexGroup, newDecorations);
this.collectDecorationData(this.repository.untrackedGroup, newDecorations);
this.collectDecorationData(this.repository.workingTreeGroup, newDecorations); this.collectDecorationData(this.repository.workingTreeGroup, newDecorations);
this.collectDecorationData(this.repository.mergeGroup, newDecorations); this.collectDecorationData(this.repository.mergeGroup, newDecorations);
......
...@@ -3,17 +3,17 @@ ...@@ -3,17 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Uri, Command, EventEmitter, Event, scm, SourceControl, SourceControlInputBox, SourceControlResourceGroup, SourceControlResourceState, SourceControlResourceDecorations, SourceControlInputBoxValidation, Disposable, ProgressLocation, window, workspace, WorkspaceEdit, ThemeColor, Decoration, Memento, SourceControlInputBoxValidationType, OutputChannel, LogLevel, env, ProgressOptions, CancellationToken } from 'vscode'; import * as fs from 'fs';
import { Repository as BaseRepository, Commit, Stash, GitError, Submodule, CommitOptions, ForcePushMode } from './git';
import { anyEvent, filterEvent, eventToPromise, dispose, find, isDescendant, IDisposable, onceEvent, EmptyDisposable, debounceEvent, combinedDisposable } from './util';
import { memoize, throttle, debounce } from './decorators';
import { toGitUri } from './uri';
import { AutoFetcher } from './autofetch';
import * as path from 'path'; 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 * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
import * as fs from 'fs'; import { Branch, Change, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status } 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 { StatusBarCommands } from './statusbar'; import { StatusBarCommands } from './statusbar';
import { Branch, Ref, Remote, RefType, GitErrorCodes, Status, LogOptions, Change } from './api/git'; import { toGitUri } from './uri';
import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util';
import { IFileWatcher, watch } from './watch'; import { IFileWatcher, watch } from './watch';
const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); const timeout = (millis: number) => new Promise(c => setTimeout(c, millis));
...@@ -33,7 +33,8 @@ export const enum RepositoryState { ...@@ -33,7 +33,8 @@ export const enum RepositoryState {
export const enum ResourceGroupType { export const enum ResourceGroupType {
Merge, Merge,
Index, Index,
WorkingTree WorkingTree,
Untracked
} }
export class Resource implements SourceControlResourceState { export class Resource implements SourceControlResourceState {
...@@ -570,6 +571,9 @@ export class Repository implements Disposable { ...@@ -570,6 +571,9 @@ export class Repository implements Disposable {
private _workingTreeGroup: SourceControlResourceGroup; private _workingTreeGroup: SourceControlResourceGroup;
get workingTreeGroup(): GitResourceGroup { return this._workingTreeGroup as GitResourceGroup; } get workingTreeGroup(): GitResourceGroup { return this._workingTreeGroup as GitResourceGroup; }
private _untrackedGroup: SourceControlResourceGroup;
get untrackedGroup(): GitResourceGroup { return this._untrackedGroup as GitResourceGroup; }
private _HEAD: Branch | undefined; private _HEAD: Branch | undefined;
get HEAD(): Branch | undefined { get HEAD(): Branch | undefined {
return this._HEAD; return this._HEAD;
...@@ -642,6 +646,7 @@ export class Repository implements Disposable { ...@@ -642,6 +646,7 @@ export class Repository implements Disposable {
this.mergeGroup.resourceStates = []; this.mergeGroup.resourceStates = [];
this.indexGroup.resourceStates = []; this.indexGroup.resourceStates = [];
this.workingTreeGroup.resourceStates = []; this.workingTreeGroup.resourceStates = [];
this.untrackedGroup.resourceStates = [];
this._sourceControl.count = 0; this._sourceControl.count = 0;
} }
...@@ -709,6 +714,7 @@ export class Repository implements Disposable { ...@@ -709,6 +714,7 @@ export class Repository implements Disposable {
this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES")); this._mergeGroup = this._sourceControl.createResourceGroup('merge', localize('merge changes', "MERGE CHANGES"));
this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES")); this._indexGroup = this._sourceControl.createResourceGroup('index', localize('staged changes', "STAGED CHANGES"));
this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES")); this._workingTreeGroup = this._sourceControl.createResourceGroup('workingTree', localize('changes', "CHANGES"));
this._untrackedGroup = this._sourceControl.createResourceGroup('untracked', localize('untracked changes', "UNTRACKED CHANGES"));
const updateIndexGroupVisibility = () => { const updateIndexGroupVisibility = () => {
const config = workspace.getConfiguration('git', root); const config = workspace.getConfiguration('git', root);
...@@ -722,11 +728,16 @@ export class Repository implements Disposable { ...@@ -722,11 +728,16 @@ export class Repository implements Disposable {
const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchSortOrder', root)); const onConfigListenerForBranchSortOrder = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchSortOrder', root));
onConfigListenerForBranchSortOrder(this.updateModelState, this, this.disposables); onConfigListenerForBranchSortOrder(this.updateModelState, this, this.disposables);
const onConfigListenerForUntracked = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.untrackedChanges', root));
onConfigListenerForUntracked(this.updateModelState, this, this.disposables);
this.mergeGroup.hideWhenEmpty = true; this.mergeGroup.hideWhenEmpty = true;
this.untrackedGroup.hideWhenEmpty = true;
this.disposables.push(this.mergeGroup); this.disposables.push(this.mergeGroup);
this.disposables.push(this.indexGroup); this.disposables.push(this.indexGroup);
this.disposables.push(this.workingTreeGroup); this.disposables.push(this.workingTreeGroup);
this.disposables.push(this.untrackedGroup);
this.disposables.push(new AutoFetcher(this, globalState)); this.disposables.push(new AutoFetcher(this, globalState));
...@@ -912,8 +923,8 @@ export class Repository implements Disposable { ...@@ -912,8 +923,8 @@ export class Repository implements Disposable {
return this.run(Operation.HashObject, () => this.repository.hashObject(data)); return this.run(Operation.HashObject, () => this.repository.hashObject(data));
} }
async add(resources: Uri[]): Promise<void> { async add(resources: Uri[], opts?: { update?: boolean }): Promise<void> {
await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.fsPath))); await this.run(Operation.Add, () => this.repository.add(resources.map(r => r.fsPath), opts));
} }
async rm(resources: Uri[]): Promise<void> { async rm(resources: Uri[]): Promise<void> {
...@@ -958,6 +969,7 @@ export class Repository implements Disposable { ...@@ -958,6 +969,7 @@ export class Repository implements Disposable {
const toClean: string[] = []; const toClean: string[] = [];
const toCheckout: string[] = []; const toCheckout: string[] = [];
const submodulesToUpdate: string[] = []; const submodulesToUpdate: string[] = [];
const resourceStates = [...this.workingTreeGroup.resourceStates, ...this.untrackedGroup.resourceStates];
resources.forEach(r => { resources.forEach(r => {
const fsPath = r.fsPath; const fsPath = r.fsPath;
...@@ -970,7 +982,7 @@ export class Repository implements Disposable { ...@@ -970,7 +982,7 @@ export class Repository implements Disposable {
} }
const raw = r.toString(); const raw = r.toString();
const scmResource = find(this.workingTreeGroup.resourceStates, sr => sr.resourceUri.toString() === raw); const scmResource = find(resourceStates, sr => sr.resourceUri.toString() === raw);
if (!scmResource) { if (!scmResource) {
return; return;
...@@ -1436,6 +1448,7 @@ export class Repository implements Disposable { ...@@ -1436,6 +1448,7 @@ export class Repository implements Disposable {
private async updateModelState(): Promise<void> { private async updateModelState(): Promise<void> {
const { status, didHitLimit } = await this.repository.getStatus(); const { status, didHitLimit } = await this.repository.getStatus();
const config = workspace.getConfiguration('git'); const config = workspace.getConfiguration('git');
const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root));
const shouldIgnore = config.get<boolean>('ignoreLimitWarning') === true; const shouldIgnore = config.get<boolean>('ignoreLimitWarning') === true;
const useIcons = !config.get<boolean>('decorations.enabled', true); const useIcons = !config.get<boolean>('decorations.enabled', true);
this.isRepositoryHuge = didHitLimit; this.isRepositoryHuge = didHitLimit;
...@@ -1496,17 +1509,29 @@ export class Repository implements Disposable { ...@@ -1496,17 +1509,29 @@ export class Repository implements Disposable {
this._submodules = submodules!; this._submodules = submodules!;
this.rebaseCommit = rebaseCommit; this.rebaseCommit = rebaseCommit;
const untrackedChanges = scopedConfig.get<'default' | 'separate' | 'hidden'>('untrackedChanges');
const index: Resource[] = []; const index: Resource[] = [];
const workingTree: Resource[] = []; const workingTree: Resource[] = [];
const merge: Resource[] = []; const merge: Resource[] = [];
const untracked: Resource[] = [];
status.forEach(raw => { status.forEach(raw => {
const uri = Uri.file(path.join(this.repository.root, raw.path)); const uri = Uri.file(path.join(this.repository.root, raw.path));
const renameUri = raw.rename ? Uri.file(path.join(this.repository.root, raw.rename)) : undefined; const renameUri = raw.rename
? Uri.file(path.join(this.repository.root, raw.rename))
: undefined;
switch (raw.x + raw.y) { switch (raw.x + raw.y) {
case '??': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.UNTRACKED, useIcons)); case '??': switch (untrackedChanges) {
case '!!': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.IGNORED, useIcons)); case 'default': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.UNTRACKED, useIcons));
case 'separate': return untracked.push(new Resource(ResourceGroupType.Untracked, uri, Status.UNTRACKED, useIcons));
default: return undefined;
}
case '!!': switch (untrackedChanges) {
case 'default': return workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.IGNORED, useIcons));
case 'separate': return untracked.push(new Resource(ResourceGroupType.Untracked, uri, Status.IGNORED, useIcons));
default: return undefined;
}
case 'DD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_DELETED, useIcons)); case 'DD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.BOTH_DELETED, useIcons));
case 'AU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_US, useIcons)); case 'AU': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.ADDED_BY_US, useIcons));
case 'UD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_THEM, useIcons)); case 'UD': return merge.push(new Resource(ResourceGroupType.Merge, uri, Status.DELETED_BY_THEM, useIcons));
...@@ -1529,6 +1554,7 @@ export class Repository implements Disposable { ...@@ -1529,6 +1554,7 @@ export class Repository implements Disposable {
case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break; case 'D': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.DELETED, useIcons, renameUri)); break;
case 'A': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break; case 'A': workingTree.push(new Resource(ResourceGroupType.WorkingTree, uri, Status.INTENT_TO_ADD, useIcons, renameUri)); break;
} }
return undefined; return undefined;
}); });
...@@ -1536,6 +1562,7 @@ export class Repository implements Disposable { ...@@ -1536,6 +1562,7 @@ export class Repository implements Disposable {
this.mergeGroup.resourceStates = merge; this.mergeGroup.resourceStates = merge;
this.indexGroup.resourceStates = index; this.indexGroup.resourceStates = index;
this.workingTreeGroup.resourceStates = workingTree; this.workingTreeGroup.resourceStates = workingTree;
this.untrackedGroup.resourceStates = untracked;
// set count badge // set count badge
this.setCountBadge(); this.setCountBadge();
...@@ -1546,12 +1573,27 @@ export class Repository implements Disposable { ...@@ -1546,12 +1573,27 @@ export class Repository implements Disposable {
} }
private setCountBadge(): void { private setCountBadge(): void {
const countBadge = workspace.getConfiguration('git').get<string>('countBadge'); const config = workspace.getConfiguration('git', Uri.file(this.repository.root));
let count = this.mergeGroup.resourceStates.length + this.indexGroup.resourceStates.length + this.workingTreeGroup.resourceStates.length; const countBadge = config.get<'all' | 'tracked' | 'off'>('countBadge');
const untrackedChanges = config.get<'default' | 'separate' | 'hidden'>('untrackedChanges');
let count =
this.mergeGroup.resourceStates.length +
this.indexGroup.resourceStates.length +
this.workingTreeGroup.resourceStates.length;
switch (countBadge) { switch (countBadge) {
case 'off': count = 0; break; case 'off': count = 0; break;
case 'tracked': count = count - this.workingTreeGroup.resourceStates.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED).length; break; case 'tracked':
if (untrackedChanges === 'default') {
count -= this.workingTreeGroup.resourceStates.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED).length;
}
break;
case 'all':
if (untrackedChanges === 'separate') {
count += this.untrackedGroup.resourceStates.length;
}
break;
} }
this._sourceControl.count = count; this._sourceControl.count = count;
...@@ -1653,7 +1695,7 @@ export class Repository implements Disposable { ...@@ -1653,7 +1695,7 @@ export class Repository implements Disposable {
const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8); const head = HEAD.name || tagName || (HEAD.commit || '').substr(0, 8);
return head return head
+ (this.workingTreeGroup.resourceStates.length > 0 ? '*' : '') + (this.workingTreeGroup.resourceStates.length + this.untrackedGroup.resourceStates.length > 0 ? '*' : '')
+ (this.indexGroup.resourceStates.length > 0 ? '+' : '') + (this.indexGroup.resourceStates.length > 0 ? '+' : '')
+ (this.mergeGroup.resourceStates.length > 0 ? '!' : ''); + (this.mergeGroup.resourceStates.length > 0 ? '!' : '');
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册