mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-22 19:29:17 +00:00
Git - add "Stashes" node to the repositories view (#279400)
* WIP - Initial implementation * Get author and committer date for a stash * Add drop stash command * More cleanup
This commit is contained in:
@@ -14,7 +14,7 @@ import { Model } from './model';
|
||||
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
|
||||
import { DiffEditorSelectionHunkToolbarContext, LineChange, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges, compareLineChanges } from './staging';
|
||||
import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri';
|
||||
import { DiagnosticSeverityConfig, dispose, fromNow, getHistoryItemDisplayName, grep, isDefined, isDescendant, isLinuxSnap, isRemote, isWindows, pathEquals, relativePath, subject, toDiagnosticSeverity, truncate } from './util';
|
||||
import { DiagnosticSeverityConfig, dispose, fromNow, getHistoryItemDisplayName, getStashDescription, grep, isDefined, isDescendant, isLinuxSnap, isRemote, isWindows, pathEquals, relativePath, subject, toDiagnosticSeverity, truncate } from './util';
|
||||
import { GitTimelineItem } from './timelineProvider';
|
||||
import { ApiRepository } from './api/api1';
|
||||
import { getRemoteSourceActions, pickRemoteSource } from './remoteSource';
|
||||
@@ -333,7 +333,7 @@ class RepositoryItem implements QuickPickItem {
|
||||
class StashItem implements QuickPickItem {
|
||||
get label(): string { return `#${this.stash.index}: ${this.stash.description}`; }
|
||||
|
||||
get description(): string | undefined { return this.stash.branchName; }
|
||||
get description(): string | undefined { return getStashDescription(this.stash); }
|
||||
|
||||
constructor(readonly stash: Stash) { }
|
||||
}
|
||||
@@ -4544,7 +4544,7 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._stashDrop(repository, stash);
|
||||
await this._stashDrop(repository, stash.index, stash.description);
|
||||
}
|
||||
|
||||
@command('git.stashDropAll', { repository: true })
|
||||
@@ -4577,15 +4577,15 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this._stashDrop(result.repository, result.stash)) {
|
||||
if (await this._stashDrop(result.repository, result.stash.index, result.stash.description)) {
|
||||
await commands.executeCommand('workbench.action.closeActiveEditor');
|
||||
}
|
||||
}
|
||||
|
||||
async _stashDrop(repository: Repository, stash: Stash): Promise<boolean> {
|
||||
async _stashDrop(repository: Repository, index: number, description: string): Promise<boolean> {
|
||||
const yes = l10n.t('Yes');
|
||||
const result = await window.showWarningMessage(
|
||||
l10n.t('Are you sure you want to drop the stash: {0}?', stash.description),
|
||||
l10n.t('Are you sure you want to drop the stash: {0}?', description),
|
||||
{ modal: true },
|
||||
yes
|
||||
);
|
||||
@@ -4593,7 +4593,7 @@ export class CommandCenter {
|
||||
return false;
|
||||
}
|
||||
|
||||
await repository.dropStash(stash.index);
|
||||
await repository.dropStash(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4606,36 +4606,7 @@ export class CommandCenter {
|
||||
return;
|
||||
}
|
||||
|
||||
const stashChanges = await repository.showStash(stash.index);
|
||||
if (!stashChanges || stashChanges.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A stash commit can have up to 3 parents:
|
||||
// 1. The first parent is the commit that was HEAD when the stash was created.
|
||||
// 2. The second parent is the commit that represents the index when the stash was created.
|
||||
// 3. The third parent (when present) represents the untracked files when the stash was created.
|
||||
const stashFirstParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`;
|
||||
const stashUntrackedFilesParentCommit = stash.parents.length === 3 ? stash.parents[2] : undefined;
|
||||
const stashUntrackedFiles: string[] = [];
|
||||
|
||||
if (stashUntrackedFilesParentCommit) {
|
||||
const untrackedFiles = await repository.getObjectFiles(stashUntrackedFilesParentCommit);
|
||||
stashUntrackedFiles.push(...untrackedFiles.map(f => path.join(repository.root, f.file)));
|
||||
}
|
||||
|
||||
const title = `Git Stash #${stash.index}: ${stash.description}`;
|
||||
const multiDiffSourceUri = toGitUri(Uri.file(repository.root), `stash@{${stash.index}}`, { scheme: 'git-stash' });
|
||||
|
||||
const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = [];
|
||||
for (const change of stashChanges) {
|
||||
const isChangeUntracked = !!stashUntrackedFiles.find(f => pathEquals(f, change.uri.fsPath));
|
||||
const modifiedUriRef = !isChangeUntracked ? stash.hash : stashUntrackedFilesParentCommit ?? stash.hash;
|
||||
|
||||
resources.push(toMultiFileDiffEditorUris(change, stashFirstParentCommit, modifiedUriRef));
|
||||
}
|
||||
|
||||
commands.executeCommand('_workbench.openMultiDiffEditor', { multiDiffSourceUri, title, resources });
|
||||
await this._viewStash(repository, stash);
|
||||
}
|
||||
|
||||
private async pickStash(repository: Repository, placeHolder: string): Promise<Stash | undefined> {
|
||||
@@ -4680,6 +4651,39 @@ export class CommandCenter {
|
||||
return { repository, stash };
|
||||
}
|
||||
|
||||
private async _viewStash(repository: Repository, stash: Stash): Promise<void> {
|
||||
const stashChanges = await repository.showStash(stash.index);
|
||||
if (!stashChanges || stashChanges.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A stash commit can have up to 3 parents:
|
||||
// 1. The first parent is the commit that was HEAD when the stash was created.
|
||||
// 2. The second parent is the commit that represents the index when the stash was created.
|
||||
// 3. The third parent (when present) represents the untracked files when the stash was created.
|
||||
const stashFirstParentCommit = stash.parents.length > 0 ? stash.parents[0] : `${stash.hash}^`;
|
||||
const stashUntrackedFilesParentCommit = stash.parents.length === 3 ? stash.parents[2] : undefined;
|
||||
const stashUntrackedFiles: string[] = [];
|
||||
|
||||
if (stashUntrackedFilesParentCommit) {
|
||||
const untrackedFiles = await repository.getObjectFiles(stashUntrackedFilesParentCommit);
|
||||
stashUntrackedFiles.push(...untrackedFiles.map(f => path.join(repository.root, f.file)));
|
||||
}
|
||||
|
||||
const title = `Git Stash #${stash.index}: ${stash.description}`;
|
||||
const multiDiffSourceUri = toGitUri(Uri.file(repository.root), `stash@{${stash.index}}`, { scheme: 'git-stash' });
|
||||
|
||||
const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = [];
|
||||
for (const change of stashChanges) {
|
||||
const isChangeUntracked = !!stashUntrackedFiles.find(f => pathEquals(f, change.uri.fsPath));
|
||||
const modifiedUriRef = !isChangeUntracked ? stash.hash : stashUntrackedFilesParentCommit ?? stash.hash;
|
||||
|
||||
resources.push(toMultiFileDiffEditorUris(change, stashFirstParentCommit, modifiedUriRef));
|
||||
}
|
||||
|
||||
commands.executeCommand('_workbench.openMultiDiffEditor', { multiDiffSourceUri, title, resources });
|
||||
}
|
||||
|
||||
@command('git.timeline.openDiff', { repository: false })
|
||||
async timelineOpenDiff(item: TimelineItem, uri: Uri | undefined, _source: string) {
|
||||
const cmd = this.resolveTimelineOpenDiffCommand(
|
||||
@@ -5217,6 +5221,78 @@ export class CommandCenter {
|
||||
await repository.deleteTag(artifact.name);
|
||||
}
|
||||
|
||||
@command('git.repositories.stashView', { repository: true })
|
||||
async artifactStashView(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
|
||||
if (!repository || !artifact) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract stash index from artifact id
|
||||
const regex = /^stash@\{(\d+)\}$/;
|
||||
const match = regex.exec(artifact.id);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stashes = await repository.getStashes();
|
||||
const stash = stashes.find(s => s.index === parseInt(match[1]));
|
||||
if (!stash) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._viewStash(repository, stash);
|
||||
}
|
||||
|
||||
@command('git.repositories.stashApply', { repository: true })
|
||||
async artifactStashApply(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
|
||||
if (!repository || !artifact) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract stash index from artifact id (format: "stash@{index}")
|
||||
const regex = /^stash@\{(\d+)\}$/;
|
||||
const match = regex.exec(artifact.id);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stashIndex = parseInt(match[1]);
|
||||
await repository.applyStash(stashIndex);
|
||||
}
|
||||
|
||||
@command('git.repositories.stashPop', { repository: true })
|
||||
async artifactStashPop(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
|
||||
if (!repository || !artifact) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract stash index from artifact id (format: "stash@{index}")
|
||||
const regex = /^stash@\{(\d+)\}$/;
|
||||
const match = regex.exec(artifact.id);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stashIndex = parseInt(match[1]);
|
||||
await repository.popStash(stashIndex);
|
||||
}
|
||||
|
||||
@command('git.repositories.stashDrop', { repository: true })
|
||||
async artifactStashDrop(repository: Repository, artifact: SourceControlArtifact): Promise<void> {
|
||||
if (!repository || !artifact) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract stash index from artifact id
|
||||
const regex = /^stash@\{(\d+)\}$/;
|
||||
const match = regex.exec(artifact.id);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._stashDrop(repository, parseInt(match[1]), artifact.name);
|
||||
}
|
||||
|
||||
private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any {
|
||||
const result = (...args: any[]) => {
|
||||
let result: Promise<any>;
|
||||
|
||||
Reference in New Issue
Block a user