Skip to content

Commit ae5c728

Browse files
committed
Adds "Incoming Activity" node to repos view - #735
Replaces "recent incoming changes" node
1 parent f39b0a6 commit ae5c728

File tree

14 files changed

+341
-111
lines changed

14 files changed

+341
-111
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
66

77
## [Unreleased]
88

9+
## Added
10+
11+
- Adds a new experimental _Incoming Activity_ node to each repository in the _Repositories_ view (enabled via `"gitlens.insiders": true`) — closes [#735](https://github.com/eamodio/vscode-gitlens/issues/735)
12+
- **Incoming Activity** — lists the recent incoming activity (merges and pulls) to your local repository
13+
- Provides the activity command, branch (if available), and date
14+
- A context menu provides access to the _Refresh_ command
15+
- Each activity expands to list the commits added by the command
16+
- An inline toolbar provides quick access to the _Compare with HEAD_ (`alt-click` for _Compare with Working Tree_), _Copy Commit ID to Clipboard_ (`alt-click` for _Copy Commit Message to Clipboard_), and _Open Commit on Remote_ (if available) commands
17+
- A context menu provides access to more common revision (commit) commands
18+
- Each revision (commit) expands to list its set of changed files, complete with status indicators for adds, changes, renames, and deletes
19+
- An inline toolbar provides quick access to the _Open File_, _Copy Commit ID to Clipboard_ (`alt-click` for _Copy Commit Message to Clipboard_), and _Open File on Remote_ (if available) commands
20+
- A context menu provides access to more common file revision commands
21+
922
## Fixed
1023

1124
- Fixes issues with the _Show More Actions_ button on the _Details_ hover not working with renamed files

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,17 @@ The repositories view provides the following features,
316316
- An inline toolbar provides quick access to the _Open File_, _Copy Commit ID to Clipboard_ (`alt-click` for _Copy Commit Message to Clipboard_), and _Open File on Remote_ (if available) commands
317317
- A context menu provides access to more common file revision commands
318318

319+
- **Incoming Activity** — lists the recent incoming activity (merges and pulls) to your local repository (experimental, enabled via `"gitlens.insiders": true`)
320+
321+
- Provides the activity command, branch (if available), and date
322+
- A context menu provides access to the _Refresh_ command
323+
- Each activity expands to list the commits added by the command
324+
- An inline toolbar provides quick access to the _Compare with HEAD_ (`alt-click` for _Compare with Working Tree_), _Copy Commit ID to Clipboard_ (`alt-click` for _Copy Commit Message to Clipboard_), and _Open Commit on Remote_ (if available) commands
325+
- A context menu provides access to more common revision (commit) commands
326+
- Each revision (commit) expands to list its set of changed files, complete with status indicators for adds, changes, renames, and deletes
327+
- An inline toolbar provides quick access to the _Open File_, _Copy Commit ID to Clipboard_ (`alt-click` for _Copy Commit Message to Clipboard_), and _Open File on Remote_ (if available) commands
328+
- A context menu provides access to more common file revision commands
329+
319330
- **Remotes** — lists the remotes in the repository
320331

321332
- Provides the name of each remote, an indicator of the direction of the remote (fetch, push, both), remote service (if applicable), and repository path

images/dark/icon-merge.svg

Lines changed: 4 additions & 0 deletions
Loading

images/light/icon-merge.svg

Lines changed: 4 additions & 0 deletions
Loading

src/git/git.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,8 +806,14 @@ export class Git {
806806
return git<string>({ cwd: repoPath }, ...params, ref1, ref2);
807807
}
808808

809-
static reflog(repoPath: string, { branch, since }: { branch?: string; since?: string } = {}): Promise<string> {
809+
static reflog(
810+
repoPath: string,
811+
{ all, branch, since }: { all?: boolean; branch?: string; since?: string } = {}
812+
): Promise<string> {
810813
const params = ['log', '-g', `--format=${GitReflogParser.defaultFormat}`, '--date=unix'];
814+
if (all) {
815+
params.push('--all');
816+
}
811817
if (branch) {
812818
params.push(branch);
813819
}

src/git/gitService.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export enum GitRepoSearchBy {
9595
}
9696

9797
const emptyPromise: Promise<GitBlame | GitDiff | GitLog | undefined> = Promise.resolve(undefined);
98+
const reflogCommands = ['merge', 'pull'];
9899

99100
export class GitService implements Disposable {
100101
private _onDidChangeRepositories = new EventEmitter<void>();
@@ -1955,17 +1956,24 @@ export class GitService implements Disposable {
19551956
}
19561957

19571958
@log()
1958-
async getRecentIncomingChanges(
1959+
async getIncomingActivity(
19591960
repoPath: string,
1960-
options: { branch?: string; since?: string } = {}
1961+
{ maxCount, ...options }: { all?: boolean; branch?: string; maxCount?: number; since?: string } = {}
19611962
): Promise<GitReflog | undefined> {
19621963
const cc = Logger.getCorrelationContext();
19631964

19641965
try {
19651966
const data = await Git.reflog(repoPath, options);
19661967
if (data === undefined) return undefined;
19671968

1968-
return GitReflogParser.parseRecentIncomingChanges(data, repoPath);
1969+
const reflog = GitReflogParser.parse(
1970+
data,
1971+
repoPath,
1972+
reflogCommands,
1973+
maxCount == null ? Container.config.advanced.maxListItems || 0 : maxCount
1974+
);
1975+
1976+
return reflog;
19691977
}
19701978
catch (ex) {
19711979
Logger.error(ex, cc);

src/git/models/reflog.ts

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,31 @@
11
'use strict';
22
import { Dates, memoize } from '../../system';
3-
import { CommitFormatting } from '../git';
3+
import { CommitFormatting, Git } from '../git';
44
import { DateStyle } from '../../config';
55

6-
export class GitReflog {
7-
previousRef: string | undefined;
6+
export interface GitReflog {
7+
readonly repoPath: string;
8+
readonly records: GitReflogRecord[];
9+
10+
readonly count: number;
11+
readonly maxCount: number | undefined;
12+
readonly truncated: boolean;
13+
}
14+
15+
export class GitReflogRecord {
16+
private _previousSha: string | undefined;
817

918
constructor(
1019
public readonly repoPath: string,
11-
public readonly ref: string,
20+
public readonly sha: string,
21+
private _selector: string,
1222
public readonly date: Date,
13-
public readonly command: string
23+
public readonly command: string,
24+
public readonly commandArgs: string | undefined,
25+
public readonly details: string | undefined
1426
) {}
1527

16-
@memoize<GitReflog['formatDate']>(format => (format == null ? 'MMMM Do, YYYY h:mma' : format))
28+
@memoize<GitReflogRecord['formatDate']>(format => (format == null ? 'MMMM Do, YYYY h:mma' : format))
1729
formatDate(format?: string | null) {
1830
if (format == null) {
1931
format = 'MMMM Do, YYYY h:mma';
@@ -32,6 +44,48 @@ export class GitReflog {
3244
: this.formatDateFromNow();
3345
}
3446

47+
@memoize()
48+
get HEAD() {
49+
if (this._selector == null || this._selector.length === 0) return '';
50+
51+
if (this._selector.startsWith('refs/heads')) {
52+
return this._selector.substr(11);
53+
}
54+
55+
if (this._selector.startsWith('refs/remotes')) {
56+
return this._selector.substr(13);
57+
}
58+
59+
return this._selector;
60+
}
61+
62+
get previousSha() {
63+
return this._previousSha;
64+
}
65+
66+
@memoize()
67+
get previousShortSha() {
68+
return Git.shortenSha(this._previousSha);
69+
}
70+
71+
get selector() {
72+
return this._selector;
73+
}
74+
75+
@memoize()
76+
get shortSha() {
77+
return Git.shortenSha(this.sha);
78+
}
79+
80+
update(previousSha?: string, selector?: string) {
81+
if (previousSha !== undefined) {
82+
this._previousSha = previousSha;
83+
}
84+
if (selector !== undefined) {
85+
this._selector = selector;
86+
}
87+
}
88+
3589
@memoize()
3690
private get dateFormatter(): Dates.DateFormatter {
3791
return Dates.getFormatter(this.date);

src/git/parsers/reflogParser.ts

Lines changed: 83 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
'use strict';
22
import { debug } from '../../system';
3-
import { GitReflog } from '../models/reflog';
3+
import { GitReflog, GitReflogRecord } from '../models/reflog';
44

5-
const incomingCommands = ['merge', 'pull'];
6-
const reflogRegex = /^<r>(.+)<d>(?:.+?)@{(.+)}<s>(\w*).*$/gm;
5+
const reflogRegex = /^<r>(.+)<d>(.+?)@{(.+)}<s>(\w*)(.*?)(?::(.*))?$/gm;
6+
// const reflogRegex = /^<r>(.+)<d>(.+?)@{(.+)}<s>(\w*)(.*?)(?::(.*))?<n>(.*)$/gm;
7+
const reflogHEADRegex = /.*?\/?HEAD$/;
78

89
// Using %x00 codes because some shells seem to try to expand things if not
910
const lb = '%x3c'; // `%x${'<'.charCodeAt(0).toString(16)}`;
@@ -14,49 +15,108 @@ export class GitReflogParser {
1415
`${lb}r${rb}%H`, // ref
1516
`${lb}d${rb}%gD`, // reflog selector (with UNIX timestamp)
1617
`${lb}s${rb}%gs` // reflog subject
18+
// `${lb}n${rb}%D` // ref names
1719
].join('');
1820

1921
@debug({ args: false })
20-
static parseRecentIncomingChanges(data: string, repoPath: string): GitReflog | undefined {
22+
static parse(data: string, repoPath: string, commands: string[], maxCount: number): GitReflog | undefined {
2123
if (!data) return undefined;
2224

23-
let reflog: GitReflog | undefined;
25+
const records: GitReflogRecord[] = [];
2426

25-
let match: RegExpExecArray | null;
27+
let sha;
28+
let selector;
2629
let date;
27-
let ref;
2830
let command;
31+
let commandArgs;
32+
let details;
33+
34+
let head;
35+
let headDate;
36+
let headSha;
37+
38+
let count = 0;
39+
let recordDate;
40+
let record: GitReflogRecord | undefined;
41+
let truncated = false;
2942

43+
let match: RegExpExecArray | null;
3044
do {
3145
match = reflogRegex.exec(data);
3246
if (match == null) break;
3347

34-
[, ref, date, command] = match;
48+
[, sha, selector, date, command, commandArgs, details] = match;
49+
50+
if (record !== undefined) {
51+
// If the next record has the same sha as the previous, use it if it is not pointing to just HEAD and the previous is
52+
if (
53+
sha === record.sha &&
54+
(date !== recordDate || !reflogHEADRegex.test(record.selector) || reflogHEADRegex.test(selector))
55+
) {
56+
continue;
57+
}
58+
59+
if (sha !== record.sha) {
60+
if (
61+
head != null &&
62+
headDate === recordDate &&
63+
headSha == record.sha &&
64+
reflogHEADRegex.test(record.selector)
65+
) {
66+
record.update(sha, head);
67+
}
68+
else {
69+
record.update(sha);
70+
}
71+
72+
records.push(record);
73+
record = undefined;
74+
recordDate = undefined;
75+
76+
count++;
77+
if (maxCount !== 0 && count >= maxCount) {
78+
truncated = true;
79+
break;
80+
}
81+
}
82+
}
3583

36-
// If we don't have a reflog, or are still at the same ref with a proper command, save it
37-
if (
38-
(reflog === undefined || (reflog !== undefined && ref === reflog.ref)) &&
39-
incomingCommands.includes(command)
40-
) {
41-
reflog = new GitReflog(
84+
if (command === 'HEAD') {
85+
head = selector;
86+
headDate = date;
87+
headSha = sha;
88+
89+
continue;
90+
}
91+
92+
if (commands.includes(command)) {
93+
record = new GitReflogRecord(
4294
repoPath,
4395
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
44-
` ${ref}`.substr(1),
45-
new Date((date! as any) * 1000),
96+
` ${sha}`.substr(1),
97+
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
98+
` ${selector}`.substr(1),
99+
new Date(Number(date) * 1000),
46100
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
47-
` ${command}`.substr(1)
101+
` ${command}`.substr(1),
102+
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
103+
commandArgs == null || commandArgs.length === 0 ? undefined : commandArgs.substr(1),
104+
// Stops excessive memory usage -- https://bugs.chromium.org/p/v8/issues/detail?id=2869
105+
details == null || details.length === 0 ? undefined : details.substr(1)
48106
);
49-
}
50-
else if (reflog !== undefined && ref !== reflog.ref) {
51-
reflog.previousRef = ref;
52-
53-
break;
107+
recordDate = date;
54108
}
55109
} while (match != null);
56110

57111
// Ensure the regex state is reset
58112
reflogRegex.lastIndex = 0;
59113

60-
return reflog;
114+
return {
115+
repoPath: repoPath,
116+
records: records,
117+
count: count,
118+
maxCount: maxCount,
119+
truncated: truncated
120+
};
61121
}
62122
}

src/views/nodes.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,21 @@ export * from './nodes/branchNode';
66
export * from './nodes/branchTrackingStatusNode';
77
export * from './nodes/commitFileNode';
88
export * from './nodes/commitNode';
9+
export * from './nodes/compareNode';
10+
export * from './nodes/compareResultsNode';
911
export * from './nodes/contributorNode';
1012
export * from './nodes/contributorsNode';
1113
export * from './nodes/fileHistoryNode';
1214
export * from './nodes/fileHistoryTrackerNode';
1315
export * from './nodes/folderNode';
1416
export * from './nodes/lineHistoryNode';
1517
export * from './nodes/lineHistoryTrackerNode';
16-
export * from './nodes/recentIncomingChangesNode';
18+
export * from './nodes/reflogNode';
19+
export * from './nodes/reflogRecordNode';
1720
export * from './nodes/remoteNode';
1821
export * from './nodes/remotesNode';
1922
export * from './nodes/repositoriesNode';
2023
export * from './nodes/repositoryNode';
21-
export * from './nodes/compareResultsNode';
22-
export * from './nodes/compareNode';
2324
export * from './nodes/resultsCommitsNode';
2425
export * from './nodes/resultsFileNode';
2526
export * from './nodes/resultsFilesNode';

0 commit comments

Comments
 (0)