diff --git a/src/git/models/commit.ts b/src/git/models/commit.ts index 144fbfb..d4851f3 100644 --- a/src/git/models/commit.ts +++ b/src/git/models/commit.ts @@ -47,10 +47,10 @@ export abstract class GitCommit implements GitRevisionReference { } static isOfRefType(commit: GitReference | undefined) { - return commit?.refType === 'revision'; + return commit?.refType === 'revision' || commit?.refType === 'stash'; } - readonly refType = 'revision'; + readonly refType: GitRevisionReference['refType'] = 'revision'; constructor( public readonly type: GitCommitType, diff --git a/src/git/models/logCommit.ts b/src/git/models/logCommit.ts index 35ed79b..b533b8e 100644 --- a/src/git/models/logCommit.ts +++ b/src/git/models/logCommit.ts @@ -1,9 +1,10 @@ 'use strict'; import { Uri } from 'vscode'; -import { memoize, Strings } from '../../system'; -import { GitUri } from '../gitUri'; import { GitCommit, GitCommitType } from './commit'; import { GitFile, GitFileStatus } from './file'; +import { GitUri } from '../gitUri'; +import { GitReference } from './models'; +import { memoize, Strings } from '../../system'; const emptyStats = Object.freeze({ added: 0, @@ -23,6 +24,10 @@ export interface GitLogCommitLine { } export class GitLogCommit extends GitCommit { + static isOfRefType(commit: GitReference | undefined) { + return commit?.refType === 'revision'; + } + static is(commit: any): commit is GitLogCommit { return ( commit instanceof GitLogCommit diff --git a/src/git/models/models.ts b/src/git/models/models.ts index 62e219f..b9903e8 100644 --- a/src/git/models/models.ts +++ b/src/git/models/models.ts @@ -101,16 +101,17 @@ export interface GitBranchReference { } export interface GitRevisionReference { - readonly refType: 'revision'; + readonly refType: 'revision' | 'stash'; name: string; ref: string; repoPath: string; + number?: string | undefined; message?: string; } export interface GitStashReference { - readonly refType: 'revision'; + readonly refType: 'stash'; name: string; ref: string; repoPath: string; @@ -167,7 +168,7 @@ export namespace GitReference { return { name: options.name, ref: ref, - refType: 'revision', + refType: 'stash', repoPath: repoPath, number: options.number, message: options.message, @@ -210,7 +211,7 @@ export namespace GitReference { } export function isStash(ref: GitReference | undefined): ref is GitStashReference { - return ref?.refType === 'revision' && (ref as any)?.stashName; + return ref?.refType === 'stash' || (ref?.refType === 'revision' && (ref as any)?.stashName); } export function isTag(ref: GitReference | undefined): ref is GitTagReference { diff --git a/src/git/models/stashCommit.ts b/src/git/models/stashCommit.ts index e27c1c5..9c808c2 100644 --- a/src/git/models/stashCommit.ts +++ b/src/git/models/stashCommit.ts @@ -2,11 +2,16 @@ import { GitCommitType } from './commit'; import { GitFile } from './file'; import { GitLogCommit } from './logCommit'; +import { GitReference } from './models'; import { memoize } from '../../system'; const stashNumberRegex = /stash@{(\d+)}/; export class GitStashCommit extends GitLogCommit { + static isOfRefType(commit: GitReference | undefined) { + return commit?.refType === 'stash'; + } + static is(commit: any): commit is GitStashCommit { return ( commit instanceof GitStashCommit @@ -16,6 +21,8 @@ export class GitStashCommit extends GitLogCommit { ); } + readonly refType = 'stash'; + constructor( type: GitCommitType, public readonly stashName: string, diff --git a/src/views/nodes/branchNode.ts b/src/views/nodes/branchNode.ts index 5effe1c..ed9ff99 100644 --- a/src/views/nodes/branchNode.ts +++ b/src/views/nodes/branchNode.ts @@ -3,7 +3,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ViewBranchesLayout } from '../../configuration'; import { GlyphChars } from '../../constants'; import { Container } from '../../container'; -import { BranchDateFormatting, GitBranch, GitLog, GitRemoteType } from '../../git/git'; +import { BranchDateFormatting, GitBranch, GitBranchReference, GitLog, GitRemoteType } from '../../git/git'; import { GitUri } from '../../git/gitUri'; import { debug, gate, Iterables, log, Strings } from '../../system'; import { RepositoriesView } from '../repositoriesView'; @@ -14,7 +14,7 @@ import { insertDateMarkers } from './helpers'; import { PageableViewNode, ResourceType, ViewNode, ViewRefNode } from './viewNode'; import { RepositoryNode } from './repositoryNode'; -export class BranchNode extends ViewRefNode implements PageableViewNode { +export class BranchNode extends ViewRefNode implements PageableViewNode { static key = ':branch'; static getId(repoPath: string, name: string, root: boolean): string { return `${RepositoryNode.getId(repoPath)}${this.key}(${name})${root ? ':root' : ''}`; @@ -56,8 +56,8 @@ export class BranchNode extends ViewRefNode implements Pageabl : this.branch.getBasename(); } - get ref(): string { - return this.branch.ref; + get ref(): GitBranchReference { + return this.branch; } get treeHierarchy(): string[] { @@ -236,7 +236,7 @@ export class BranchNode extends ViewRefNode implements Pageabl if (this._log === undefined) { this._log = await Container.git.getLog(this.uri.repoPath!, { limit: this.limit ?? this.view.config.defaultItemLimit, - ref: this.ref, + ref: this.ref.ref, }); } diff --git a/src/views/nodes/commitFileNode.ts b/src/views/nodes/commitFileNode.ts index 45057ca..0b4c235 100644 --- a/src/views/nodes/commitFileNode.ts +++ b/src/views/nodes/commitFileNode.ts @@ -4,7 +4,7 @@ import { Command, Selection, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { Commands, DiffWithPreviousCommandArgs } from '../../commands'; import { GlyphChars } from '../../constants'; import { Container } from '../../container'; -import { CommitFormatter, GitFile, GitLogCommit, StatusFileFormatter } from '../../git/git'; +import { CommitFormatter, GitFile, GitLogCommit, GitRevisionReference, StatusFileFormatter } from '../../git/git'; import { GitUri } from '../../git/gitUri'; import { View } from '../viewBase'; import { ResourceType, ViewNode, ViewRefFileNode } from './viewNode'; @@ -32,8 +32,8 @@ export class CommitFileNode extends ViewRefFileNode { return 0; } - get ref(): string { - return this.commit.sha; + get ref(): GitRevisionReference { + return this.commit; } getChildren(): ViewNode[] { diff --git a/src/views/nodes/commitNode.ts b/src/views/nodes/commitNode.ts index 2bdca88..73e95ed 100644 --- a/src/views/nodes/commitNode.ts +++ b/src/views/nodes/commitNode.ts @@ -5,14 +5,14 @@ import { Commands, DiffWithPreviousCommandArgs } from '../../commands'; import { ViewFilesLayout } from '../../configuration'; import { GlyphChars } from '../../constants'; import { Container } from '../../container'; -import { CommitFormatter, GitBranch, GitLogCommit } from '../../git/git'; +import { CommitFormatter, GitBranch, GitLogCommit, GitRevisionReference } from '../../git/git'; import { Arrays, Iterables, Strings } from '../../system'; import { ViewWithFiles } from '../viewBase'; import { CommitFileNode } from './commitFileNode'; import { FileNode, FolderNode } from './folderNode'; import { ResourceType, ViewNode, ViewRefNode } from './viewNode'; -export class CommitNode extends ViewRefNode { +export class CommitNode extends ViewRefNode { constructor( view: ViewWithFiles, parent: ViewNode, @@ -28,8 +28,8 @@ export class CommitNode extends ViewRefNode { return this.commit.sha; } - get ref(): string { - return this.commit.sha; + get ref(): GitRevisionReference { + return this.commit; } getChildren(): ViewNode[] { diff --git a/src/views/nodes/resultsFileNode.ts b/src/views/nodes/resultsFileNode.ts index 8ce9ca8..415d3af 100644 --- a/src/views/nodes/resultsFileNode.ts +++ b/src/views/nodes/resultsFileNode.ts @@ -3,7 +3,7 @@ import * as paths from 'path'; import { Command, TreeItem, TreeItemCollapsibleState } from 'vscode'; import { Commands, DiffWithCommandArgs } from '../../commands'; import { Container } from '../../container'; -import { GitFile, StatusFileFormatter } from '../../git/git'; +import { GitFile, GitReference, GitRevisionReference, StatusFileFormatter } from '../../git/git'; import { GitUri } from '../../git/gitUri'; import { View } from '../viewBase'; import { ResourceType, ViewNode, ViewRefFileNode } from './viewNode'; @@ -28,8 +28,8 @@ export class ResultsFileNode extends ViewRefFileNode { return this.file.fileName; } - get ref() { - return this.ref1 || this.ref2; + get ref(): GitRevisionReference { + return GitReference.create(this.ref1 || this.ref2, this.uri.repoPath!); } getChildren(): ViewNode[] { diff --git a/src/views/nodes/stashNode.ts b/src/views/nodes/stashNode.ts index 495f647..1b63125 100644 --- a/src/views/nodes/stashNode.ts +++ b/src/views/nodes/stashNode.ts @@ -2,7 +2,7 @@ import * as paths from 'path'; import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { Container } from '../../container'; -import { CommitFormatter, GitStashCommit } from '../../git/git'; +import { CommitFormatter, GitStashCommit, GitStashReference } from '../../git/git'; import { Arrays, Iterables, Strings } from '../../system'; import { ViewWithFiles } from '../viewBase'; import { StashFileNode } from './stashFileNode'; @@ -11,7 +11,7 @@ import { RepositoryNode } from './repositoryNode'; import { FileNode, FolderNode } from '../nodes'; import { ViewFilesLayout } from '../../config'; -export class StashNode extends ViewRefNode { +export class StashNode extends ViewRefNode { static key = ':stash'; static getId(repoPath: string, ref: string): string { return `${RepositoryNode.getId(repoPath)}${this.key}(${ref})`; @@ -29,8 +29,8 @@ export class StashNode extends ViewRefNode { return StashNode.getId(this.commit.repoPath, this.commit.sha); } - get ref(): string { - return this.commit.sha; + get ref(): GitStashReference { + return this.commit; } async getChildren(): Promise { diff --git a/src/views/nodes/tagNode.ts b/src/views/nodes/tagNode.ts index 6e54fde..c335a3d 100644 --- a/src/views/nodes/tagNode.ts +++ b/src/views/nodes/tagNode.ts @@ -2,7 +2,7 @@ import { TreeItem, TreeItemCollapsibleState } from 'vscode'; import { ViewBranchesLayout } from '../../configuration'; import { Container } from '../../container'; -import { GitLog, GitRevision, GitTag, TagDateFormatting } from '../../git/git'; +import { GitLog, GitRevision, GitTag, GitTagReference, TagDateFormatting } from '../../git/git'; import { GitUri } from '../../git/gitUri'; import { debug, gate, Iterables, Strings } from '../../system'; import { RepositoriesView } from '../repositoriesView'; @@ -14,7 +14,7 @@ import { emojify } from '../../emojis'; import { RepositoryNode } from './repositoryNode'; import { GlyphChars } from '../../constants'; -export class TagNode extends ViewRefNode implements PageableViewNode { +export class TagNode extends ViewRefNode implements PageableViewNode { static key = ':tag'; static getId(repoPath: string, name: string): string { return `${RepositoryNode.getId(repoPath)}${this.key}(${name})`; @@ -36,8 +36,8 @@ export class TagNode extends ViewRefNode implements PageableVi return this.view.config.branches.layout === ViewBranchesLayout.Tree ? this.tag.getBasename() : this.tag.name; } - get ref(): string { - return this.tag.name; + get ref(): GitTagReference { + return this.tag; } async getChildren(): Promise { diff --git a/src/views/nodes/viewNode.ts b/src/views/nodes/viewNode.ts index 5a79e09..34a8b5d 100644 --- a/src/views/nodes/viewNode.ts +++ b/src/views/nodes/viewNode.ts @@ -1,6 +1,6 @@ 'use strict'; import { Command, Disposable, Event, TreeItem, TreeItemCollapsibleState, TreeViewVisibilityChangeEvent } from 'vscode'; -import { GitFile } from '../../git/git'; +import { GitFile, GitReference, GitRevisionReference } from '../../git/git'; import { GitUri } from '../../git/gitUri'; import { Logger } from '../../logger'; import { debug, Functions, gate, logName } from '../../system'; @@ -98,19 +98,22 @@ export abstract class ViewNode { } } -export abstract class ViewRefNode extends ViewNode { - abstract get ref(): string; +export abstract class ViewRefNode< + TView extends View = View, + TReference extends GitReference = GitReference +> extends ViewNode { + abstract get ref(): TReference; get repoPath(): string { return this.uri.repoPath!; } toString() { - return `${super.toString()}:${this.ref}`; + return `${super.toString()}:${GitReference.toString(this.ref, false)}`; } } -export abstract class ViewRefFileNode extends ViewRefNode { +export abstract class ViewRefFileNode extends ViewRefNode { abstract get file(): GitFile; abstract get fileName(): string; diff --git a/src/views/viewCommands.ts b/src/views/viewCommands.ts index 1234e78..7389e3e 100644 --- a/src/views/viewCommands.ts +++ b/src/views/viewCommands.ts @@ -197,16 +197,16 @@ export class ViewCommands { ); } - if (node.ref == null || node.ref === 'HEAD') return Promise.resolve(); + if (node.ref == null || node.ref.ref === 'HEAD') return Promise.resolve(); - return GitActions.Commit.applyChanges(node.file, GitReference.create(node.ref, node.repoPath)); + return GitActions.Commit.applyChanges(node.file, node.ref); } @debug() private cherryPick(node: CommitNode) { if (!(node instanceof CommitNode)) return Promise.resolve(); - return GitActions.cherryPick(node.repoPath, GitReference.create(node.ref, node.repoPath)); + return GitActions.cherryPick(node.repoPath, node.ref); } @debug() @@ -237,17 +237,14 @@ export class ViewCommands { private createBranch(node: ViewRefNode) { if (!(node instanceof ViewRefNode)) return Promise.resolve(); - return GitActions.Branch.create( - node.repoPath, - node instanceof BranchNode ? node.branch : GitReference.create(node.ref, node.repoPath), - ); + return GitActions.Branch.create(node.repoPath, node.ref); } @debug() private createTag(node: ViewRefNode) { if (!(node instanceof ViewRefNode)) return Promise.resolve(); - return GitActions.Tag.create(node.repoPath, GitReference.create(node.ref, node.repoPath)); + return GitActions.Tag.create(node.repoPath, node.ref); } @debug() @@ -300,7 +297,7 @@ export class ViewCommands { void (await Container.fileAnnotations.toggle( window.activeTextEditor, FileAnnotationType.Changes, - node.ref, + node.ref.ref, true, )); } @@ -319,7 +316,7 @@ export class ViewCommands { void (await Container.fileAnnotations.toggle( window.activeTextEditor, FileAnnotationType.Changes, - node.ref, + node.ref.ref, true, )); } @@ -378,14 +375,7 @@ export class ViewCommands { return Promise.resolve(); } - return GitActions.rebase( - node.repoPath, - node instanceof CommitNode - ? GitReference.create(node.ref, node.repoPath) - : node instanceof BranchNode - ? node.branch - : node.tag, - ); + return GitActions.rebase(node.repoPath, node.ref); } @debug() @@ -416,21 +406,21 @@ export class ViewCommands { private reset(node: CommitNode) { if (!(node instanceof CommitNode)) return Promise.resolve(); - return GitActions.reset(node.repoPath, GitReference.create(node.ref, node.repoPath)); + return GitActions.reset(node.repoPath, node.ref); } @debug() private restore(node: ViewRefFileNode) { if (!(node instanceof ViewRefFileNode)) return Promise.resolve(); - return GitActions.Commit.restoreFile(node.fileName, GitReference.create(node.ref, node.repoPath)); + return GitActions.Commit.restoreFile(node.fileName, node.ref); } @debug() private revert(node: CommitNode) { if (!(node instanceof CommitNode)) return Promise.resolve(); - return GitActions.revert(node.repoPath, GitReference.create(node.ref, node.repoPath)); + return GitActions.revert(node.repoPath, node.ref); } @debug() @@ -476,13 +466,7 @@ export class ViewCommands { return GitActions.switchTo( node.repoPath, - node instanceof BranchNode - ? node.branch.current - ? undefined - : node.branch - : node instanceof TagNode - ? node.tag - : GitReference.create(node.ref, node.repoPath), + node instanceof BranchNode && node.branch.current ? undefined : node.ref, ); } @@ -545,12 +529,12 @@ export class ViewCommands { const branch = await Container.git.getBranch(node.repoPath); if (branch == null) return undefined; - const commonAncestor = await Container.git.getMergeBase(node.repoPath, branch.ref, node.ref); + const commonAncestor = await Container.git.getMergeBase(node.repoPath, branch.ref, node.ref.ref); if (commonAncestor == null) return undefined; return Container.compareView.compare( node.repoPath, - { ref: commonAncestor, label: `ancestry with ${node.ref} (${GitRevision.shorten(commonAncestor)})` }, + { ref: commonAncestor, label: `ancestry with ${node.ref.ref} (${GitRevision.shorten(commonAncestor)})` }, '', ); } @@ -592,7 +576,7 @@ export class ViewCommands { uri: selected.uri!, }, rhs: { - sha: node.ref, + sha: node.ref.ref, uri: node.uri, }, }); @@ -605,7 +589,7 @@ export class ViewCommands { if (!(node instanceof ViewRefFileNode) || node.ref == null) return; this._selectedFile = { - ref: node.ref, + ref: node.ref.ref, repoPath: node.repoPath, uri: node.uri, }; @@ -746,7 +730,7 @@ export class ViewCommands { }); } - return GitActions.Commit.openChangesWithWorking(node.file, { repoPath: node.repoPath, ref: node.ref }); + return GitActions.Commit.openChangesWithWorking(node.file, { repoPath: node.repoPath, ref: node.ref.ref }); } @debug()