diff --git a/package.json b/package.json index 805b4fccb30ba..37edede7a64ce 100644 --- a/package.json +++ b/package.json @@ -1743,6 +1743,13 @@ "title": "Commits View", "order": 110, "properties": { + "gitlens.views.commits.showStashes": { + "type": "boolean", + "default": false, + "markdownDescription": "Specifies whether to show stashes in the _Commits_ view", + "scope": "window", + "order": 9 + }, "gitlens.views.commits.showBranchComparison": { "type": [ "boolean", @@ -2070,7 +2077,7 @@ }, "gitlens.views.repositories.showStashes": { "type": "boolean", - "default": true, + "default": false, "markdownDescription": "Specifies whether to show the stashes for each repository in the _Repositories_ view", "scope": "window", "order": 33 @@ -2192,6 +2199,13 @@ "scope": "window", "order": 90 }, + "gitlens.views.repositories.branches.showStashes": { + "type": "boolean", + "default": false, + "markdownDescription": "Specifies whether to show stashes in the _Commits_ and _Branches_ sections of the _Repositories_ view", + "scope": "window", + "order": 95 + }, "gitlens.views.repositories.branches.showBranchComparison": { "type": [ "boolean", @@ -2355,6 +2369,13 @@ "title": "Branches View", "order": 170, "properties": { + "gitlens.views.branches.showStashes": { + "type": "boolean", + "default": false, + "markdownDescription": "Specifies whether to show stashes in the _Branches_ view", + "scope": "window", + "order": 9 + }, "gitlens.views.branches.showBranchComparison": { "type": [ "boolean", @@ -2756,6 +2777,13 @@ "scope": "resource", "order": 12 }, + "gitlens.views.worktrees.showStashes": { + "type": "boolean", + "default": false, + "markdownDescription": "Specifies whether to show stashes in the _Worktrees_ view", + "scope": "window", + "order": 19 + }, "gitlens.views.worktrees.showBranchComparison": { "type": [ "boolean", @@ -3229,7 +3257,7 @@ }, "gitlens.views.workspaces.showStashes": { "type": "boolean", - "default": true, + "default": false, "markdownDescription": "Specifies whether to show the stashes for each repository in the _Cloud Workspaces_ view", "scope": "window", "order": 33 @@ -3337,6 +3365,13 @@ "scope": "window", "order": 90 }, + "gitlens.views.workspaces.branches.showStashes": { + "type": "boolean", + "default": false, + "markdownDescription": "Specifies whether to show stashes in the _Commits_ and _Branches_ sections of the _Cloud Workspaces_ view", + "scope": "window", + "order": 9 + }, "gitlens.views.workspaces.branches.showBranchComparison": { "type": [ "boolean", @@ -7673,6 +7708,14 @@ "command": "gitlens.views.branches.setShowBranchPullRequestOff", "title": "Hide Branch Pull Requests" }, + { + "command": "gitlens.views.branches.setShowStashesOn", + "title": "Show Stashes" + }, + { + "command": "gitlens.views.branches.setShowStashesOff", + "title": "Hide Stashes" + }, { "command": "gitlens.views.commitDetails.refresh", "title": "Refresh", @@ -7759,6 +7802,14 @@ "command": "gitlens.views.commits.setShowBranchPullRequestOff", "title": "Hide Current Branch Pull Request" }, + { + "command": "gitlens.views.commits.setShowStashesOn", + "title": "Show Stashes" + }, + { + "command": "gitlens.views.commits.setShowStashesOff", + "title": "Hide Stashes" + }, { "command": "gitlens.views.contributors.copy", "title": "Copy" @@ -8804,6 +8855,14 @@ "command": "gitlens.views.worktrees.setShowBranchPullRequestOff", "title": "Hide Branch Pull Requests" }, + { + "command": "gitlens.views.worktrees.setShowStashesOn", + "title": "Show Stashes" + }, + { + "command": "gitlens.views.worktrees.setShowStashesOff", + "title": "Hide Stashes" + }, { "command": "gitlens.enableDebugLogging", "title": "Enable Debug Logging", @@ -11224,6 +11283,14 @@ "command": "gitlens.views.branches.setShowBranchPullRequestOff", "when": "false" }, + { + "command": "gitlens.views.branches.setShowStashesOn", + "when": "false" + }, + { + "command": "gitlens.views.branches.setShowStashesOff", + "when": "false" + }, { "command": "gitlens.views.commitDetails.refresh", "when": "false" @@ -11300,6 +11367,14 @@ "command": "gitlens.views.commits.setShowBranchPullRequestOff", "when": "false" }, + { + "command": "gitlens.views.commits.setShowStashesOn", + "when": "false" + }, + { + "command": "gitlens.views.commits.setShowStashesOff", + "when": "false" + }, { "command": "gitlens.views.contributors.copy", "when": "false" @@ -12196,6 +12271,14 @@ "command": "gitlens.views.worktrees.setShowBranchPullRequestOff", "when": "false" }, + { + "command": "gitlens.views.worktrees.setShowStashesOn", + "when": "false" + }, + { + "command": "gitlens.views.worktrees.setShowStashesOff", + "when": "false" + }, { "command": "gitlens.graph.switchToEditorLayout", "when": "gitlens:enabled && config.gitlens.graph.layout != editor" @@ -13229,6 +13312,16 @@ "when": "view == gitlens.views.branches && config.gitlens.views.branches.pullRequests.enabled && config.gitlens.views.branches.pullRequests.showForBranches", "group": "5_gitlens@2" }, + { + "command": "gitlens.views.branches.setShowStashesOn", + "when": "view == gitlens.views.branches && !config.gitlens.views.branches.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, + { + "command": "gitlens.views.branches.setShowStashesOff", + "when": "view == gitlens.views.branches && config.gitlens.views.branches.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, { "command": "gitlens.pushRepositories", "when": "gitlens:repos:withRemotes && !gitlens:readonly && !gitlens:untrusted && !gitlens:hasVirtualFolders && view == gitlens.views.commits", @@ -13337,7 +13430,17 @@ { "command": "gitlens.views.commits.setShowBranchPullRequestOff", "when": "view == gitlens.views.commits && config.gitlens.views.commits.pullRequests.enabled && config.gitlens.views.commits.pullRequests.showForBranches", - "group": "5_gitlens@4" + "group": "5_gitlens@3" + }, + { + "command": "gitlens.views.commits.setShowStashesOn", + "when": "view == gitlens.views.commits && !config.gitlens.views.commits.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, + { + "command": "gitlens.views.commits.setShowStashesOff", + "when": "view == gitlens.views.commits && config.gitlens.views.commits.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" }, { "command": "gitlens.showGraph", @@ -14289,15 +14392,25 @@ "when": "view == gitlens.views.worktrees && config.gitlens.views.worktrees.pullRequests.enabled && config.gitlens.views.worktrees.pullRequests.showForBranches", "group": "5_gitlens@2" }, + { + "command": "gitlens.views.worktrees.setShowStashesOn", + "when": "view == gitlens.views.worktrees && !config.gitlens.views.worktrees.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, + { + "command": "gitlens.views.worktrees.setShowStashesOff", + "when": "view == gitlens.views.worktrees && config.gitlens.views.worktrees.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, { "command": "gitlens.views.setShowRelativeDateMarkersOn", "when": "view =~ /^gitlens\\.views\\.(branches|commits|contributors|fileHistory|lineHistory|remotes|repositories|tags|worktrees)/ && !config.gitlens.views.showRelativeDateMarkers", - "group": "5_gitlens@3" + "group": "5_gitlens@10" }, { "command": "gitlens.views.setShowRelativeDateMarkersOff", "when": "view =~ /^gitlens\\.views\\.(branches|commits|contributors|fileHistory|lineHistory|remotes|repositories|tags|worktrees)/ && config.gitlens.views.showRelativeDateMarkers", - "group": "5_gitlens@3" + "group": "5_gitlens@10" }, { "submenu": "gitlens/graph/configuration", @@ -17421,12 +17534,12 @@ }, { "command": "gitlens.views.repositories.setShowStashesOn", - "when": "!config.gitlens.views.repositories.showStashes", + "when": "!config.gitlens.views.repositories.showStashes && !gitlens:hasVirtualFolders", "group": "2_gitlens@5" }, { "command": "gitlens.views.repositories.setShowStashesOff", - "when": "config.gitlens.views.repositories.showStashes", + "when": "config.gitlens.views.repositories.showStashes && !gitlens:hasVirtualFolders", "group": "2_gitlens@5" }, { @@ -17756,12 +17869,12 @@ { "command": "gitlens.views.setShowRelativeDateMarkersOn", "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view =~ /(branches|commits|contributors|remotes|repositories|tags|worktrees)/ && !config.gitlens.views.showRelativeDateMarkers", - "group": "5_gitlens@3" + "group": "5_gitlens@10" }, { "command": "gitlens.views.setShowRelativeDateMarkersOff", "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view =~ /(branches|commits|contributors|remotes|repositories|tags|worktrees)/ && config.gitlens.views.showRelativeDateMarkers", - "group": "5_gitlens@3" + "group": "5_gitlens@10" }, { "command": "gitlens.views.branches.setLayoutToList", @@ -17818,6 +17931,16 @@ "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == branches && config.gitlens.views.branches.pullRequests.enabled && config.gitlens.views.branches.pullRequests.showForBranches", "group": "5_gitlens@2" }, + { + "command": "gitlens.views.branches.setShowStashesOn", + "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == branches && !config.gitlens.views.branches.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, + { + "command": "gitlens.views.branches.setShowStashesOff", + "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == branches && config.gitlens.views.branches.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, { "command": "gitlens.showSettingsPage!branches-view", "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == branches", @@ -17876,7 +17999,17 @@ { "command": "gitlens.views.commits.setShowBranchPullRequestOff", "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == commits && config.gitlens.views.commits.pullRequests.enabled && config.gitlens.views.commits.pullRequests.showForBranches", - "group": "5_gitlens@4" + "group": "5_gitlens@3" + }, + { + "command": "gitlens.views.commits.setShowStashesOn", + "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == commits && !config.gitlens.views.commits.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, + { + "command": "gitlens.views.commits.setShowStashesOff", + "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == commits && config.gitlens.views.commits.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" }, { "command": "gitlens.showSettingsPage!commits-view", @@ -18198,6 +18331,16 @@ "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == worktrees && config.gitlens.views.worktrees.pullRequests.enabled && config.gitlens.views.worktrees.pullRequests.showForBranches", "group": "5_gitlens@2" }, + { + "command": "gitlens.views.worktrees.setShowStashesOn", + "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == worktrees && !config.gitlens.views.worktrees.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, + { + "command": "gitlens.views.worktrees.setShowStashesOff", + "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == worktrees && config.gitlens.views.worktrees.showStashes && !gitlens:hasVirtualFolders", + "group": "5_gitlens@11" + }, { "command": "gitlens.showSettingsPage!worktrees-view", "when": "view == gitlens.views.scm.grouped && gitlens:views:scm:grouped:view == worktrees", diff --git a/src/config.ts b/src/config.ts index e30c6b95c9be2..e2259e1b08e33 100644 --- a/src/config.ts +++ b/src/config.ts @@ -700,6 +700,7 @@ export interface BranchesViewConfig { }; readonly reveal: boolean; readonly showBranchComparison: false | Extract; + readonly showStashes: boolean; } export interface CommitsViewConfig { @@ -713,6 +714,7 @@ export interface CommitsViewConfig { }; readonly reveal: boolean; readonly showBranchComparison: false | ViewShowBranchComparison; + readonly showStashes: boolean; } export interface CommitDetailsViewConfig { @@ -806,6 +808,7 @@ export interface RepositoriesViewConfig { readonly branches: { readonly layout: ViewBranchesLayout; readonly showBranchComparison: false | Extract; + readonly showStashes: boolean; }; readonly compact: boolean; readonly files: ViewsFilesConfig; @@ -860,6 +863,7 @@ export interface WorktreesViewConfig { }; readonly reveal: boolean; readonly showBranchComparison: false | Extract; + readonly showStashes: boolean; } export interface WorkspacesViewConfig { @@ -867,6 +871,7 @@ export interface WorkspacesViewConfig { readonly branches: { readonly layout: ViewBranchesLayout; readonly showBranchComparison: false | Extract; + readonly showStashes: boolean; }; readonly compact: boolean; readonly files: ViewsFilesConfig; diff --git a/src/constants.commands.ts b/src/constants.commands.ts index 8207aeedcb384..fe6e5d08e1f8c 100644 --- a/src/constants.commands.ts +++ b/src/constants.commands.ts @@ -337,7 +337,8 @@ export type TreeViewCommands = `gitlens.views.${ | `setFilesLayoutTo${'Auto' | 'List' | 'Tree'}` | `setShowAvatars${'On' | 'Off'}` | `setShowBranchComparison${'On' | 'Off'}` - | `setShowBranchPullRequest${'On' | 'Off'}`}` + | `setShowBranchPullRequest${'On' | 'Off'}` + | `setShowStashes${'On' | 'Off'}`}` | `commits.${ | 'copy' | 'refresh' @@ -346,7 +347,8 @@ export type TreeViewCommands = `gitlens.views.${ | `setShowAvatars${'On' | 'Off'}` | `setShowBranchComparison${'On' | 'Off'}` | `setShowBranchPullRequest${'On' | 'Off'}` - | `setShowMergeCommits${'On' | 'Off'}`}` + | `setShowMergeCommits${'On' | 'Off'}` + | `setShowStashes${'On' | 'Off'}`}` | `contributors.${ | 'copy' | 'refresh' @@ -451,7 +453,8 @@ export type TreeViewCommands = `gitlens.views.${ | `setFilesLayoutTo${'Auto' | 'List' | 'Tree'}` | `setShowAvatars${'On' | 'Off'}` | `setShowBranchComparison${'On' | 'Off'}` - | `setShowBranchPullRequest${'On' | 'Off'}`}`}`; + | `setShowBranchPullRequest${'On' | 'Off'}` + | `setShowStashes${'On' | 'Off'}`}`}`; type ExtractSuffix = U extends `${Prefix}${infer V}` ? V : never; type FilterCommands = U extends `${Prefix}${infer V}` ? `${Prefix}${V}` : never; diff --git a/src/env/node/git/git.ts b/src/env/node/git/git.ts index f109e990e0532..eb9f6f5a2da73 100644 --- a/src/env/node/git/git.ts +++ b/src/env/node/git/git.ts @@ -1591,7 +1591,7 @@ export class Git { async rev_list( repoPath: string, ref: string, - options?: { all?: boolean; maxParents?: number }, + options?: { all?: boolean; maxParents?: number; since?: string }, ): Promise { const params = ['rev-list']; if (options?.all) { @@ -1602,6 +1602,10 @@ export class Git { params.push(`--max-parents=${options.maxParents}`); } + if (options?.since) { + params.push(`--since="${options.since}"`, '--date-order'); + } + const rawData = await this.git( { cwd: repoPath, errors: GitErrorHandling.Ignore }, ...params, diff --git a/src/env/node/git/localGitProvider.ts b/src/env/node/git/localGitProvider.ts index 20a16c579551f..a678d0ad27d65 100644 --- a/src/env/node/git/localGitProvider.ts +++ b/src/env/node/git/localGitProvider.ts @@ -188,7 +188,17 @@ import { countStringLength, filterMap } from '../../../system/array'; import { gate } from '../../../system/decorators/gate'; import { debug, log } from '../../../system/decorators/log'; import { debounce } from '../../../system/function'; -import { filterMap as filterMapIterable, find, first, join, last, map, skip, some } from '../../../system/iterable'; +import { + filterMap as filterMapIterable, + find, + first, + join, + last, + map, + min, + skip, + some, +} from '../../../system/iterable'; import { Logger } from '../../../system/logger'; import type { LogScope } from '../../../system/logger.scope'; import { getLogScope, setLogScopeExit } from '../../../system/logger.scope'; @@ -3658,11 +3668,11 @@ export class LocalGitProvider implements GitProvider, Disposable { merges?: boolean | 'first-parent'; ordering?: 'date' | 'author-date' | 'topo' | null; ref?: string; - status?: null | 'name-status' | 'numstat' | 'stat'; since?: number | string; + stashes?: boolean | Map; + status?: null | 'name-status' | 'numstat' | 'stat'; until?: number | string; extraArgs?: string[]; - stdin?: string; }, ): Promise { const scope = getLogScope(); @@ -3727,9 +3737,41 @@ export class LocalGitProvider implements GitProvider, Disposable { args.push(`-n${limit + 1}`); } + let ref = options?.ref; + + let stashes: Map | undefined; + let stdin: string | undefined; + + if (options?.stashes) { + if (typeof options.stashes === 'boolean') { + // TODO@eamodio this is insanity -- there *HAS* to be a better way to get git log to return stashes + const gitStash = await this.getStash(repoPath, { reachableFrom: options?.ref }); + stashes = new Map(gitStash?.stashes); + if (gitStash?.stashes.size) { + stdin = ''; + for (const stash of gitStash.stashes.values()) { + stdin += `${stash.sha.substring(0, 9)}\n`; + // Include the stash's 2nd (index files) and 3rd (untracked files) parents + for (const p of skip(stash.parents, 1)) { + stashes.set(p, stash); + stdin += `${p.substring(0, 9)}\n`; + } + } + } + ref ??= 'HEAD'; + } else { + stashes = options.stashes; + stdin = join( + map(stashes.values(), c => c.sha.substring(0, 9)), + '\n', + ); + ref ??= 'HEAD'; + } + } + const data = await this.git.log( repoPath, - { configs: gitLogDefaultConfigsWithFiles, ref: options?.ref, stdin: options?.stdin }, + { configs: gitLogDefaultConfigsWithFiles, ref: ref, stdin: stdin }, ...args, ); @@ -3739,12 +3781,12 @@ export class LocalGitProvider implements GitProvider, Disposable { LogType.Log, repoPath, undefined, - options?.ref, + ref, await this.getCurrentUser(repoPath), limit, false, undefined, - undefined, + stashes, undefined, hasMoreOverride, ); @@ -4956,7 +4998,7 @@ export class LocalGitProvider implements GitProvider, Disposable { @gate() @log() - async getStash(repoPath: string | undefined): Promise { + async getStash(repoPath: string | undefined, options?: { reachableFrom?: string }): Promise { if (repoPath == null) return undefined; let gitStash = this.useCaching ? this._stashesCache.get(repoPath) : undefined; @@ -5034,6 +5076,48 @@ export class LocalGitProvider implements GitProvider, Disposable { } } + // Return only reachable stashes from the given ref + if (options?.reachableFrom && gitStash != null) { + const oldestStashDate = new Date(min(gitStash.stashes.values(), c => c.date.getTime())).toISOString(); + + const ancestors = await this.git.rev_list(repoPath, options.reachableFrom, { since: oldestStashDate }); + if (ancestors?.length && (ancestors.length !== 1 || ancestors[0])) { + const reachableCommits = new Set(ancestors); + + if (reachableCommits.size) { + const reachableStashes = new Set(); + + // First pass: mark directly reachable stashes + for (const [sha, stash] of gitStash.stashes) { + if (stash.parents.some(p => p === options.reachableFrom || reachableCommits.has(p))) { + reachableStashes.add(sha); + } + } + + // Second pass: mark stashes that build upon reachable stashes + let changed; + do { + changed = false; + for (const [sha, stash] of gitStash.stashes) { + if (!reachableStashes.has(sha) && stash.parents.some(p => reachableStashes.has(p))) { + reachableStashes.add(sha); + changed = true; + } + } + } while (changed); + + // Remove unreachable stashes + for (const [sha] of gitStash.stashes) { + if (!reachableStashes.has(sha)) { + gitStash.stashes.delete(sha); + } + } + } else { + gitStash.stashes.clear(); + } + } + } + return gitStash ?? undefined; } diff --git a/src/git/gitProviderService.ts b/src/git/gitProviderService.ts index aeb41768e11f0..b9bbe56687749 100644 --- a/src/git/gitProviderService.ts +++ b/src/git/gitProviderService.ts @@ -2029,6 +2029,7 @@ export class GitProviderService implements Disposable { ordering?: 'date' | 'author-date' | 'topo' | null; ref?: string; since?: string; + stashes?: boolean; }, ): Promise { const { provider, path } = this.getProvider(repoPath); diff --git a/src/git/models/commit.ts b/src/git/models/commit.ts index 63f41d3aa9347..5def499fe8877 100644 --- a/src/git/models/commit.ts +++ b/src/git/models/commit.ts @@ -226,7 +226,7 @@ export class GitCommit implements GitRevisionReference { } const [commitResult, untrackedResult] = await Promise.allSettled([ - this.refType !== 'stash' ? this.container.git.getCommit(this.repoPath, this.sha) : undefined, + this.container.git.getCommit(this.repoPath, this.sha), // Check for any untracked files -- since git doesn't return them via `git stash list` :( // See https://stackoverflow.com/questions/12681529/ this.refType === 'stash' && !this._stashUntrackedFilesLoaded diff --git a/src/views/branchesView.ts b/src/views/branchesView.ts index 886601096349e..f95bec7da55e5 100644 --- a/src/views/branchesView.ts +++ b/src/views/branchesView.ts @@ -178,6 +178,8 @@ export class BranchesView extends ViewBase<'branches', BranchesViewNode, Branche () => this.setShowBranchPullRequest(false), this, ), + registerViewCommand(this.getQualifiedCommand('setShowStashesOn'), () => this.setShowStashes(true), this), + registerViewCommand(this.getQualifiedCommand('setShowStashesOff'), () => this.setShowStashes(false), this), ]; } @@ -353,4 +355,8 @@ export class BranchesView extends ViewBase<'branches', BranchesViewNode, Branche await configuration.updateEffective(`views.${this.configKey}.pullRequests.showForBranches` as const, enabled); await configuration.updateEffective(`views.${this.configKey}.pullRequests.enabled` as const, enabled); } + + private setShowStashes(enabled: boolean) { + return configuration.updateEffective(`views.${this.configKey}.showStashes` as const, enabled); + } } diff --git a/src/views/commitsView.ts b/src/views/commitsView.ts index 5f09b5a026290..da351a497eed1 100644 --- a/src/views/commitsView.ts +++ b/src/views/commitsView.ts @@ -57,6 +57,7 @@ export class CommitsRepositoryNode extends RepositoryFolderNode this.setShowBranchPullRequest(false), this, ), + registerViewCommand(this.getQualifiedCommand('setShowStashesOn'), () => this.setShowStashes(true), this), + registerViewCommand(this.getQualifiedCommand('setShowStashesOff'), () => this.setShowStashes(false), this), ]; } @@ -509,4 +512,8 @@ export class CommitsView extends ViewBase<'commits', CommitsViewNode, CommitsVie await configuration.updateEffective(`views.${this.configKey}.pullRequests.showForBranches` as const, enabled); await configuration.updateEffective(`views.${this.configKey}.pullRequests.enabled` as const, enabled); } + + private setShowStashes(enabled: boolean) { + return configuration.updateEffective(`views.${this.configKey}.showStashes` as const, enabled); + } } diff --git a/src/views/nodes/branchNode.ts b/src/views/nodes/branchNode.ts index 1a37e583a8569..058d711a0752b 100644 --- a/src/views/nodes/branchNode.ts +++ b/src/views/nodes/branchNode.ts @@ -9,6 +9,7 @@ import type { GitUri } from '../../git/gitUri'; import { unknownGitUri } from '../../git/gitUri'; import type { GitBranch } from '../../git/models/branch'; import { getTargetBranchName } from '../../git/models/branch'; +import { isStash } from '../../git/models/commit'; import type { GitLog } from '../../git/models/log'; import type { PullRequest, PullRequestState } from '../../git/models/pullRequest'; import type { GitBranchReference } from '../../git/models/reference'; @@ -44,6 +45,7 @@ import { insertDateMarkers } from './helpers'; import { MergeStatusNode } from './mergeStatusNode'; import { PullRequestNode } from './pullRequestNode'; import { RebaseStatusNode } from './rebaseStatusNode'; +import { StashNode } from './stashNode'; type State = { pullRequest: PullRequest | null | undefined; @@ -57,6 +59,7 @@ type Options = { showComparison: false | ViewShowBranchComparison; showStatusDecorationOnly: boolean; showMergeCommits?: boolean; + showStashes: boolean; showStatus: boolean; showTracking: boolean; authors?: GitUser[]; @@ -92,6 +95,7 @@ export class BranchNode limitCommits: false, showAsCommits: false, showComparison: false, + showStashes: false, // Only show status decorations when the node is displayed as a root showStatusDecorationOnly: this.root, // Don't show merge/rebase status info the node is displayed as a root @@ -386,17 +390,17 @@ export class BranchNode children.push( ...insertDateMarkers( - map( - log.commits.values(), - c => - new CommitNode( - this.view, - this, - c, - unpublishedCommits?.has(c.ref), - branch, - getBranchAndTagTips, - ), + map(log.commits.values(), c => + isStash(c) + ? new StashNode(this.view, this, c, { icon: true }) + : new CommitNode( + this.view, + this, + c, + unpublishedCommits?.has(c.ref), + branch, + getBranchAndTagTips, + ), ), this, ), @@ -510,6 +514,7 @@ export class BranchNode ref: this.ref.ref, authors: this.options?.authors, merges: this.options?.showMergeCommits, + stashes: this.options?.showStashes, }); } diff --git a/src/views/nodes/branchesNode.ts b/src/views/nodes/branchesNode.ts index 8034455e9becb..032d869142d61 100644 --- a/src/views/nodes/branchesNode.ts +++ b/src/views/nodes/branchesNode.ts @@ -62,6 +62,7 @@ export class BranchesNode extends CacheableChildrenViewNode<'branches', ViewsWit this.view.type === 'repositories' ? this.view.config.branches.showBranchComparison : this.view.config.showBranchComparison, + showStashes: this.view.config.showStashes, }, ), ); diff --git a/src/views/nodes/remoteNode.ts b/src/views/nodes/remoteNode.ts index cbf0f1842a394..fa4e819817e6e 100644 --- a/src/views/nodes/remoteNode.ts +++ b/src/views/nodes/remoteNode.ts @@ -52,6 +52,7 @@ export class RemoteNode extends ViewNode<'remote', ViewsWithRemotes> { b => new BranchNode(GitUri.fromRepoPath(this.uri.repoPath!, b.ref), this.view, this, this.repo, b, false, { showComparison: false, + showStashes: false, showTracking: false, }), ); diff --git a/src/views/nodes/repositoryNode.ts b/src/views/nodes/repositoryNode.ts index 93cceb98070f8..94c87865f2936 100644 --- a/src/views/nodes/repositoryNode.ts +++ b/src/views/nodes/repositoryNode.ts @@ -159,6 +159,7 @@ export class RepositoryNode extends SubscribeableViewNode<'repository', ViewsWit new BranchNode(this.uri, this.view, this, this.repo, branch, true, { showAsCommits: true, showComparison: false, + showStashes: this.view.config.branches.showStashes, showStatusDecorationOnly: true, showStatus: false, showTracking: false, diff --git a/src/views/nodes/worktreeNode.ts b/src/views/nodes/worktreeNode.ts index 3c9a84c4f3254..0baea06fa1709 100644 --- a/src/views/nodes/worktreeNode.ts +++ b/src/views/nodes/worktreeNode.ts @@ -3,6 +3,7 @@ import type { IconPath } from '../../@types/vscode.iconpath'; import { GlyphChars } from '../../constants'; import type { GitUri } from '../../git/gitUri'; import type { GitBranch } from '../../git/models/branch'; +import { isStash } from '../../git/models/commit'; import type { GitLog } from '../../git/models/log'; import type { PullRequest, PullRequestState } from '../../git/models/pullRequest'; import { shortenRevision } from '../../git/models/reference'; @@ -27,6 +28,7 @@ import { LoadMoreNode, MessageNode } from './common'; import { CompareBranchNode } from './compareBranchNode'; import { insertDateMarkers } from './helpers'; import { PullRequestNode } from './pullRequestNode'; +import { StashNode } from './stashNode'; import { UncommittedFilesNode } from './UncommittedFilesNode'; type State = { @@ -158,17 +160,17 @@ export class WorktreeNode extends CacheableChildrenViewNode<'worktree', ViewsWit children.push( ...insertDateMarkers( - map( - log.commits.values(), - c => - new CommitNode( - this.view, - this, - c, - unpublishedCommits?.has(c.ref), - branch, - getBranchAndTagTips, - ), + map(log.commits.values(), c => + isStash(c) + ? new StashNode(this.view, this, c, { icon: true }) + : new CommitNode( + this.view, + this, + c, + unpublishedCommits?.has(c.ref), + branch, + getBranchAndTagTips, + ), ), this, ), @@ -403,6 +405,7 @@ export class WorktreeNode extends CacheableChildrenViewNode<'worktree', ViewsWit this._log = await this.view.container.git.getLog(this.uri.repoPath!, { ref: this.worktree.sha, limit: this.limit ?? this.view.config.defaultItemLimit, + stashes: this.view.config.showStashes, }); } diff --git a/src/views/worktreesView.ts b/src/views/worktreesView.ts index 5160d7d71c57d..eecf923bd5b1b 100644 --- a/src/views/worktreesView.ts +++ b/src/views/worktreesView.ts @@ -175,6 +175,8 @@ export class WorktreesView extends ViewBase<'worktrees', WorktreesViewNode, Work () => this.setShowBranchPullRequest(false), this, ), + registerViewCommand(this.getQualifiedCommand('setShowStashesOn'), () => this.setShowStashes(true), this), + registerViewCommand(this.getQualifiedCommand('setShowStashesOff'), () => this.setShowStashes(false), this), ]; } @@ -280,4 +282,8 @@ export class WorktreesView extends ViewBase<'worktrees', WorktreesViewNode, Work await configuration.updateEffective(`views.${this.configKey}.pullRequests.showForBranches` as const, enabled); await configuration.updateEffective(`views.${this.configKey}.pullRequests.enabled` as const, enabled); } + + private setShowStashes(enabled: boolean) { + return configuration.updateEffective(`views.${this.configKey}.showStashes` as const, enabled); + } }