diff --git a/src/git/git.ts b/src/git/git.ts index 92b1995..47f6636 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -25,6 +25,7 @@ const logFormat = [ `${lb}a${rb} %aN`, // author `${lb}e${rb} %aE`, // email `${lb}d${rb} %at`, // date + `${lb}c${rb} %ct`, // committed date `${lb}p${rb} %P`, // parents `${lb}s${rb}`, `%B`, // summary @@ -38,6 +39,7 @@ const stashFormat = [ `${lb}${sl}f${rb}`, `${lb}r${rb} %H`, // ref `${lb}d${rb} %at`, // date + `${lb}c${rb} %ct`, // committed date `${lb}l${rb} %gd`, // reflog-selector `${lb}s${rb}`, `%B`, // summary diff --git a/src/git/models/logCommit.ts b/src/git/models/logCommit.ts index a3c1016..8699d57 100644 --- a/src/git/models/logCommit.ts +++ b/src/git/models/logCommit.ts @@ -17,6 +17,7 @@ export class GitLogCommit extends GitCommit { author: string, email: string | undefined, date: Date, + public readonly committedDate: Date, message: string, fileName: string, public readonly files: GitFile[], @@ -168,6 +169,7 @@ export class GitLogCommit extends GitCommit { author?: string; email?: string; date?: Date; + committedDate?: Date; message?: string; originalFileName?: string | null; previousFileName?: string | null; @@ -182,6 +184,7 @@ export class GitLogCommit extends GitCommit { changes.author || this.author, changes.email || this.email, changes.date || this.date, + changes.committedDate || this.committedDate, changes.message || this.message, changes.fileName || this.fileName, this.getChangedValue(changes.files, this.files) || [], diff --git a/src/git/models/stashCommit.ts b/src/git/models/stashCommit.ts index ce396a4..d2fe1b2 100644 --- a/src/git/models/stashCommit.ts +++ b/src/git/models/stashCommit.ts @@ -10,6 +10,7 @@ export class GitStashCommit extends GitLogCommit { repoPath: string, sha: string, date: Date, + committedDate: Date, message: string, fileName: string, files: GitFile[], @@ -25,6 +26,7 @@ export class GitStashCommit extends GitLogCommit { 'You', undefined, date, + committedDate, message, fileName, files, @@ -44,6 +46,7 @@ export class GitStashCommit extends GitLogCommit { sha?: string | null; fileName?: string; date?: Date; + committedDate?: Date; message?: string; originalFileName?: string | null; previousFileName?: string | null; @@ -57,6 +60,7 @@ export class GitStashCommit extends GitLogCommit { this.repoPath, this.getChangedValue(changes.sha, this.sha)!, changes.date || this.date, + changes.committedDate || this.committedDate, changes.message || this.message, changes.fileName || this.fileName, this.getChangedValue(changes.files, this.files) || [], diff --git a/src/git/parsers/logParser.ts b/src/git/parsers/logParser.ts index 3a7c279..ba4f164 100644 --- a/src/git/parsers/logParser.ts +++ b/src/git/parsers/logParser.ts @@ -9,6 +9,7 @@ interface LogEntry { author?: string; date?: string; + committedDate?: string; email?: string; parentShas?: string[]; @@ -98,6 +99,10 @@ export class GitLogParser { entry.date = line.substring(4); break; + case 99: // 'c': // committer-date + entry.committedDate = line.substring(4); + break; + case 112: // 'p': // parents entry.parentShas = line.substring(4).split(' '); break; @@ -283,6 +288,7 @@ export class GitLogParser { entry.author!, entry.email, new Date((entry.date! as any) * 1000), + new Date((entry.committedDate! as any) * 1000), entry.summary === undefined ? '' : entry.summary, relativeFileName, entry.files || [], diff --git a/src/git/parsers/stashParser.ts b/src/git/parsers/stashParser.ts index 15862a6..64536b6 100644 --- a/src/git/parsers/stashParser.ts +++ b/src/git/parsers/stashParser.ts @@ -6,6 +6,7 @@ import { GitCommitType, GitFile, GitFileStatus, GitLogParser, GitStash, GitStash interface StashEntry { ref?: string; date?: string; + committedDate?: string; fileNames?: string; files?: GitFile[]; summary?: string; @@ -54,6 +55,10 @@ export class GitStashParser { entry.date = line.substring(4); break; + case 99: // 'c': // committer-date + entry.committedDate = line.substring(4); + break; + case 108: // 'l': // reflog-selector entry.stashName = line.substring(4); break; @@ -140,6 +145,7 @@ export class GitStashParser { repoPath, entry.ref!, new Date((entry.date! as any) * 1000), + new Date((entry.committedDate! as any) * 1000), entry.summary === undefined ? '' : entry.summary, entry.fileNames!, entry.files || [] diff --git a/src/quickpicks/repoStatusQuickPick.ts b/src/quickpicks/repoStatusQuickPick.ts index aa37146..fe5af84 100644 --- a/src/quickpicks/repoStatusQuickPick.ts +++ b/src/quickpicks/repoStatusQuickPick.ts @@ -58,6 +58,7 @@ export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPick 'You', undefined, new Date(), + new Date(), '', status.fileName, [status], @@ -75,6 +76,7 @@ export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPick 'You', undefined, new Date(), + new Date(), '', status.fileName, [status], diff --git a/src/views/nodes/branchNode.ts b/src/views/nodes/branchNode.ts index a0bf7b7..66c85bc 100644 --- a/src/views/nodes/branchNode.ts +++ b/src/views/nodes/branchNode.ts @@ -9,6 +9,7 @@ import { RepositoriesExplorer } from '../repositoriesExplorer'; import { CommitNode } from './commitNode'; import { MessageNode, ShowMoreNode } from './common'; import { ExplorerNode, ExplorerRefNode, PageableExplorerNode, ResourceType } from './explorerNode'; +import { insertDateMarkers } from './helpers'; export class BranchNode extends ExplorerRefNode implements PageableExplorerNode { readonly supportsPaging: boolean = true; @@ -70,10 +71,13 @@ export class BranchNode extends ExplorerRefNode implements PageableExplorerNode return branches.join(', '); }; - const children: (CommitNode | ShowMoreNode)[] = [ - ...Iterables.map( - log.commits.values(), - c => new CommitNode(c, this, this.explorer, this.branch, getBranchTips) + const children = [ + ...insertDateMarkers( + Iterables.map( + log.commits.values(), + c => new CommitNode(c, this, this.explorer, this.branch, getBranchTips) + ), + this ) ]; diff --git a/src/views/nodes/fileHistoryNode.ts b/src/views/nodes/fileHistoryNode.ts index 878586e..b6799b5 100644 --- a/src/views/nodes/fileHistoryNode.ts +++ b/src/views/nodes/fileHistoryNode.ts @@ -16,6 +16,7 @@ import { FileHistoryExplorer } from '../fileHistoryExplorer'; import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode'; import { MessageNode } from './common'; import { ExplorerNode, ResourceType, SubscribeableExplorerNode } from './explorerNode'; +import { insertDateMarkers } from './helpers'; export class FileHistoryNode extends SubscribeableExplorerNode { constructor(uri: GitUri, parent: ExplorerNode, explorer: FileHistoryExplorer) { @@ -55,6 +56,7 @@ export class FileHistoryNode extends SubscribeableExplorerNode new CommitFileNode(c.files[0], c, this, this.explorer, displayAs) + ...insertDateMarkers( + Iterables.map( + log.commits.values(), + c => new CommitFileNode(c.files[0], c, this, this.explorer, displayAs) + ), + this ) ); } diff --git a/src/views/nodes/helpers.ts b/src/views/nodes/helpers.ts new file mode 100644 index 0000000..a212be5 --- /dev/null +++ b/src/views/nodes/helpers.ts @@ -0,0 +1,53 @@ +'use strict'; +import { GitLogCommit } from '../../git/gitService'; +import { MessageNode } from './common'; +import { ExplorerNode } from './explorerNode'; + +const markers: [number, string][] = [ + [0, 'Less than a week ago'], + [7, 'Over a week ago'], + [30, 'Over a month ago'], + [90, 'Over 3 months ago'] +]; + +export function* insertDateMarkers( + iterable: Iterable, + parent: ExplorerNode, + skip?: number +): Iterable { + let index = skip || 0; + let time = undefined; + const now = Date.now(); + + for (const node of iterable) { + if (index < markers.length) { + let [daysAgo, marker] = markers[index]; + if (time === undefined) { + const date = new Date(now); + time = date.setDate(date.getDate() - daysAgo); + } + + const date = new Date(node.commit.committedDate.setUTCHours(0, 0, 0, 0)).getTime(); + if (date <= time) { + while (index < markers.length - 1) { + [daysAgo] = markers[index + 1]; + const nextDate = new Date(now); + const nextTime = nextDate.setDate(nextDate.getDate() - daysAgo); + + if (date > nextTime) break; + + index++; + time = undefined; + [, marker] = markers[index]; + } + + yield new MessageNode(parent, marker); + + index++; + time = undefined; + } + } + + yield node; + } +} diff --git a/src/views/nodes/lineHistoryNode.ts b/src/views/nodes/lineHistoryNode.ts index 6e3758a..6fbb533 100644 --- a/src/views/nodes/lineHistoryNode.ts +++ b/src/views/nodes/lineHistoryNode.ts @@ -15,6 +15,7 @@ import { LineHistoryExplorer } from '../lineHistoryExplorer'; import { CommitFileNode, CommitFileNodeDisplayAs } from './commitFileNode'; import { MessageNode } from './common'; import { ExplorerNode, ResourceType, SubscribeableExplorerNode } from './explorerNode'; +import { insertDateMarkers } from './helpers'; export class LineHistoryNode extends SubscribeableExplorerNode { constructor( @@ -39,16 +40,22 @@ export class LineHistoryNode extends SubscribeableExplorerNode new CommitFileNode(c.files[0], c, this, this.explorer, displayAs, this.selection) + ...insertDateMarkers( + Iterables.filterMap( + log.commits.values(), + c => new CommitFileNode(c.files[0], c, this, this.explorer, displayAs, this.selection) + ), + this ) ); } const blame = await Container.git.getBlameForLine(this.uri, this.selection.active.line); if (blame !== undefined) { - const first = children[0] as CommitFileNode | undefined; + let first = children[0] as CommitFileNode | undefined; + if (first !== undefined && !(first instanceof CommitFileNode)) { + first = children[1] as CommitFileNode | undefined; + } if (first === undefined || first.commit.sha !== blame.commit.sha) { const file: GitFile = { @@ -67,6 +74,8 @@ export class LineHistoryNode extends SubscribeableExplorerNode new CommitNode(c, this, this.explorer)); + children = [...insertDateMarkers(commits.map(c => new CommitNode(c, this, this.explorer)), this, 1)]; } else { - children = [...Iterables.map(log.commits.values(), c => new CommitNode(c, this, this.explorer))]; + children = [ + ...insertDateMarkers( + Iterables.map(log.commits.values(), c => new CommitNode(c, this, this.explorer)), + this, + 1 + ) + ]; } if (log.truncated) { diff --git a/src/views/nodes/tagNode.ts b/src/views/nodes/tagNode.ts index 6ea0bb8..86281c9 100644 --- a/src/views/nodes/tagNode.ts +++ b/src/views/nodes/tagNode.ts @@ -8,6 +8,7 @@ import { RepositoriesExplorer } from '../repositoriesExplorer'; import { CommitNode } from './commitNode'; import { MessageNode, ShowMoreNode } from './common'; import { ExplorerNode, ExplorerRefNode, PageableExplorerNode, ResourceType } from './explorerNode'; +import { insertDateMarkers } from './helpers'; export class TagNode extends ExplorerRefNode implements PageableExplorerNode { readonly supportsPaging: boolean = true; @@ -43,8 +44,8 @@ export class TagNode extends ExplorerRefNode implements PageableExplorerNode { }); if (log === undefined) return [new MessageNode(this, 'No commits yet')]; - const children: (CommitNode | ShowMoreNode)[] = [ - ...Iterables.map(log.commits.values(), c => new CommitNode(c, this, this.explorer)) + const children = [ + ...insertDateMarkers(Iterables.map(log.commits.values(), c => new CommitNode(c, this, this.explorer)), this) ]; if (log.truncated) {