Browse Source

Adds detailed tracking status to all branches in the repos view

main
Eric Amodio 6 years ago
parent
commit
cdc2241279
11 changed files with 167 additions and 121 deletions
  1. +5
    -0
      CHANGELOG.md
  2. +4
    -0
      README.md
  3. +6
    -4
      src/git/models/branch.ts
  4. +2
    -7
      src/git/models/status.ts
  5. +1
    -1
      src/views/nodes.ts
  6. +27
    -6
      src/views/nodes/branchNode.ts
  7. +25
    -8
      src/views/nodes/branchTrackingStatusNode.ts
  8. +4
    -4
      src/views/nodes/repositoryNode.ts
  9. +3
    -1
      src/views/nodes/viewNode.ts
  10. +7
    -7
      src/views/viewCommands.ts

+ 5
- 0
CHANGELOG.md View File

@ -8,6 +8,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Added
- Adds more detailed branch tracking status (if available) to the **Branches** list in the _Repositories_ view
- **\* Commits Behind** — quickly see and explore the specific commits behind the upstream (i.e. commits that haven't been pulled)
- Only provided if the current branch is tracking a remote branch and is behind it
- **\* Commits Ahead** — quickly see and explore the specific commits ahead of the upstream (i.e. commits that haven't been pushed)
- Only provided if the current branch is tracking a remote branch and is ahead of it
- Adds control over the contributed menu commands to the Source Control side bar to the GitLens interactive settings editor (via the `gitlens.menus` setting)
- Adds Git extended regex support to commit searches

+ 4
- 0
README.md View File

@ -329,6 +329,10 @@ The repositories view provides the following features,
- An inline toolbar provides quick access to the _Checkout_, _Compare with Remote_ (if available), _Compare with HEAD_ (`alt-click` for _Compare with Working Tree_), and _Open Branch on Remote_ (if available) commands
- A context menu provides access to more common branch commands
- Each branch expands to list its revision (commit) history
- **\* Commits Behind** — quickly see and explore the specific commits behind the upstream (i.e. commits that haven't been pulled)
- Only provided if the current branch is tracking a remote branch and is behind it
- **\* Commits Ahead** — quickly see and explore the specific commits ahead of the upstream (i.e. commits that haven't been pushed)
- Only provided if the current branch is tracking a remote branch and is ahead of it
- 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
- A context menu provides access to more common revision (commit) commands
- Each revision (commit) expands to list its set of changed files, complete with status indicators for adds, changes, renames, and deletes

+ 6
- 4
src/git/models/branch.ts View File

@ -2,15 +2,17 @@
import { Git } from '../git';
import { GitStatus } from './status';
export interface GitTrackingState {
ahead: number;
behind: number;
}
export class GitBranch {
readonly detached: boolean;
readonly name: string;
readonly remote: boolean;
readonly tracking?: string;
readonly state: {
ahead: number;
behind: number;
};
readonly state: GitTrackingState;
constructor(
public readonly repoPath: string,

+ 2
- 7
src/git/models/status.ts View File

@ -3,14 +3,9 @@ import { Uri } from 'vscode';
import { GlyphChars } from '../../constants';
import { Strings } from '../../system';
import { GitUri } from '../gitUri';
import { GitBranch } from './branch';
import { GitBranch, GitTrackingState } from './branch';
import { GitFile, GitFileStatus } from './file';
export interface GitStatusUpstreamState {
ahead: number;
behind: number;
}
export class GitStatus {
readonly detached: boolean;
@ -19,7 +14,7 @@ export class GitStatus {
public readonly branch: string,
public readonly sha: string,
public readonly files: GitStatusFile[],
public readonly state: GitStatusUpstreamState,
public readonly state: GitTrackingState,
public readonly upstream?: string
) {
this.detached = GitBranch.isDetached(branch);

+ 1
- 1
src/views/nodes.ts View File

@ -3,6 +3,7 @@
export * from './nodes/viewNode';
export * from './nodes/branchesNode';
export * from './nodes/branchNode';
export * from './nodes/branchTrackingStatusNode';
export * from './nodes/commitFileNode';
export * from './nodes/commitNode';
export * from './nodes/fileHistoryNode';
@ -24,6 +25,5 @@ export * from './nodes/stashFileNode';
export * from './nodes/stashNode';
export * from './nodes/statusFileNode';
export * from './nodes/statusFilesNode';
export * from './nodes/statusUpstreamNode';
export * from './nodes/tagsNode';
export * from './nodes/tagNode';

+ 27
- 6
src/views/nodes/branchNode.ts View File

@ -6,6 +6,7 @@ import { Container } from '../../container';
import { GitBranch, GitUri } from '../../git/gitService';
import { Iterables } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { BranchTrackingStatusNode } from './branchTrackingStatusNode';
import { CommitNode } from './commitNode';
import { MessageNode, ShowMoreNode } from './common';
import { getBranchesAndTagTipsFn, insertDateMarkers } from './helpers';
@ -22,15 +23,16 @@ export class BranchNode extends ViewRefNode implements Pageabl
view: RepositoriesView,
parent: ViewNode,
public readonly branch: GitBranch,
private readonly _markCurrent: boolean = true
// Specifies that the node is shown as a root under the repository node
private readonly _root: boolean = false
) {
super(uri, view, parent);
}
get id(): string {
return `gitlens:repository(${this.branch.repoPath}):branch(${this.branch.name})${
return `gitlens:repository(${this.branch.repoPath}):${this._root ? 'root:' : ''}branch(${this.branch.name})${
this.branch.remote ? ':remote' : ''
}${this._markCurrent ? ':current' : ''}`;
}`;
}
get current(): boolean {
@ -50,6 +52,24 @@ export class BranchNode extends ViewRefNode implements Pageabl
async getChildren(): Promise<ViewNode[]> {
if (this._children === undefined) {
const children = [];
if (!this._root && this.branch.tracking) {
const status = {
ref: this.branch.ref,
repoPath: this.branch.repoPath,
state: this.branch.state,
upstream: this.branch.tracking
};
if (this.branch.state.behind) {
children.push(new BranchTrackingStatusNode(this.view, this, status, 'behind'));
}
if (this.branch.state.ahead) {
children.push(new BranchTrackingStatusNode(this.view, this, status, 'ahead'));
}
}
const log = await Container.git.getLog(this.uri.repoPath!, {
maxCount: this.maxCount || this.view.config.defaultItemLimit,
ref: this.ref
@ -57,7 +77,7 @@ export class BranchNode extends ViewRefNode implements Pageabl
if (log === undefined) return [new MessageNode(this.view, this, 'No commits could be found.')];
const getBranchAndTagTips = await getBranchesAndTagTipsFn(this.uri.repoPath, this.branch.name);
const children = [
children.push(
...insertDateMarkers(
Iterables.map(
log.commits.values(),
@ -65,7 +85,7 @@ export class BranchNode extends ViewRefNode implements Pageabl
),
this
)
];
);
if (log.truncated) {
children.push(new ShowMoreNode(this.view, this, 'Commits'));
@ -104,7 +124,8 @@ export class BranchNode extends ViewRefNode implements Pageabl
}
const item = new TreeItem(
`${this._markCurrent && this.current ? `${GlyphChars.Check} ${GlyphChars.Space}` : ''}${name}`,
// Hide the current branch checkmark when the node is displayed as a root under the repository node
`${!this._root && this.current ? `${GlyphChars.Check} ${GlyphChars.Space}` : ''}${name}`,
TreeItemCollapsibleState.Collapsed
);
item.id = this.id;

src/views/nodes/statusUpstreamNode.ts → src/views/nodes/branchTrackingStatusNode.ts View File

@ -1,30 +1,40 @@
'use strict';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { Container } from '../../container';
import { GitStatus, GitUri } from '../../git/gitService';
import { GitTrackingState, GitUri } from '../../git/gitService';
import { Iterables, Strings } from '../../system';
import { View } from '../viewBase';
import { CommitNode } from './commitNode';
import { ShowMoreNode } from './common';
import { insertDateMarkers } from './helpers';
import { RepositoryNode } from './repositoryNode';
import { PageableViewNode, ResourceType, ViewNode } from './viewNode';
export class StatusUpstreamNode extends ViewNode implements PageableViewNode {
export interface BranchTrackingStatus {
ref: string;
repoPath: string;
state: GitTrackingState;
upstream?: string;
}
export class BranchTrackingStatusNode extends ViewNode implements PageableViewNode {
readonly supportsPaging: boolean = true;
maxCount: number | undefined;
constructor(
view: View,
parent: RepositoryNode,
public readonly status: GitStatus,
public readonly direction: 'ahead' | 'behind'
parent: ViewNode,
public readonly status: BranchTrackingStatus,
public readonly direction: 'ahead' | 'behind',
// Specifies that the node is shown as a root under the repository node
private readonly _root: boolean = false
) {
super(GitUri.fromRepoPath(status.repoPath), view, parent);
}
get id(): string {
return `gitlens:repository(${this.status.repoPath}):status:upstream:${this.direction}`;
return `gitlens:repository(${this.status.repoPath}):${this._root ? 'root:' : ''}branch(${
this.status.ref
}):status:upstream:(${this.status.upstream}):${this.direction}`;
}
async getChildren(): Promise<ViewNode[]> {
@ -77,7 +87,14 @@ export class StatusUpstreamNode extends ViewNode implements PageableViewNode {
const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.id = this.id;
item.contextValue = ahead ? ResourceType.StatusAheadOfUpstream : ResourceType.StatusBehindUpstream;
if (this._root) {
item.contextValue = ahead ? ResourceType.StatusAheadOfUpstream : ResourceType.StatusBehindUpstream;
}
else {
item.contextValue = ahead
? ResourceType.BranchStatusAheadOfUpstream
: ResourceType.BranchStatusBehindUpstream;
}
item.tooltip = `${label}${ahead ? ' of ' : ''}${this.status.upstream}`;
const iconSuffix = ahead ? 'upload' : 'download';

+ 4
- 4
src/views/nodes/repositoryNode.ts View File

@ -15,11 +15,11 @@ import { Dates, debug, Functions, gate, log, Strings } from '../../system';
import { RepositoriesView } from '../repositoriesView';
import { BranchesNode } from './branchesNode';
import { BranchNode } from './branchNode';
import { BranchTrackingStatusNode } from './branchTrackingStatusNode';
import { MessageNode } from './common';
import { RemotesNode } from './remotesNode';
import { StashesNode } from './stashesNode';
import { StatusFilesNode } from './statusFilesNode';
import { StatusUpstreamNode } from './statusUpstreamNode';
import { TagsNode } from './tagsNode';
import { ResourceType, SubscribeableViewNode, ViewNode } from './viewNode';
@ -59,14 +59,14 @@ export class RepositoryNode extends SubscribeableViewNode {
status.state.behind,
status.detached
);
children.push(new BranchNode(this.uri, this.view, this, branch, false));
children.push(new BranchNode(this.uri, this.view, this, branch, true));
if (status.state.behind) {
children.push(new StatusUpstreamNode(this.view, this, status, 'behind'));
children.push(new BranchTrackingStatusNode(this.view, this, status, 'behind', true));
}
if (status.state.ahead) {
children.push(new StatusUpstreamNode(this.view, this, status, 'ahead'));
children.push(new BranchTrackingStatusNode(this.view, this, status, 'ahead', true));
}
if (status.state.ahead || (status.files.length !== 0 && this.includeWorkingTree)) {

+ 3
- 1
src/views/nodes/viewNode.ts View File

@ -9,9 +9,11 @@ export enum ResourceType {
ActiveFileHistory = 'gitlens:history:active:file',
ActiveLineHistory = 'gitlens:history:active:line',
Branch = 'gitlens:branch',
BranchWithTracking = 'gitlens:branch:tracking',
Branches = 'gitlens:branches',
BranchesWithRemotes = 'gitlens:branches:remotes',
BranchStatusAheadOfUpstream = 'gitlens:branch-status:upstream:ahead',
BranchStatusBehindUpstream = 'gitlens:branch-status:upstream:behind',
BranchWithTracking = 'gitlens:branch:tracking',
CurrentBranch = 'gitlens:branch:current',
CurrentBranchWithTracking = 'gitlens:branch:current:tracking',
RemoteBranch = 'gitlens:branch:remote',

+ 7
- 7
src/views/viewCommands.ts View File

@ -19,6 +19,7 @@ import { GitService, GitUri } from '../git/gitService';
import { Arrays } from '../system';
import {
BranchNode,
BranchTrackingStatusNode,
canDismissNode,
CommitFileNode,
CommitNode,
@ -28,7 +29,6 @@ import {
StashFileNode,
StashNode,
StatusFileNode,
StatusUpstreamNode,
TagNode,
ViewNode,
ViewRefNode
@ -134,8 +134,8 @@ export class ViewCommands implements Disposable {
return;
}
private pull(node: RepositoryNode | StatusUpstreamNode) {
if (node instanceof StatusUpstreamNode) {
private pull(node: RepositoryNode | BranchTrackingStatusNode) {
if (node instanceof BranchTrackingStatusNode) {
node = node.getParent() as RepositoryNode;
}
if (!(node instanceof RepositoryNode)) return;
@ -143,8 +143,8 @@ export class ViewCommands implements Disposable {
return node.pull();
}
private push(node: RepositoryNode | StatusUpstreamNode, force?: boolean) {
if (node instanceof StatusUpstreamNode) {
private push(node: RepositoryNode | BranchTrackingStatusNode, force?: boolean) {
if (node instanceof BranchTrackingStatusNode) {
node = node.getParent() as RepositoryNode;
}
if (!(node instanceof RepositoryNode)) return;
@ -502,13 +502,13 @@ export class ViewCommands implements Disposable {
this.sendTerminalCommand('rebase', `-i ${node.ref}`, node.repoPath);
}
terminalRebaseBranchToRemote(node: BranchNode | StatusUpstreamNode) {
terminalRebaseBranchToRemote(node: BranchNode | BranchTrackingStatusNode) {
if (node instanceof BranchNode) {
if (!node.branch.current || !node.branch.tracking) return;
this.sendTerminalCommand('rebase', `-i ${node.branch.tracking}`, node.repoPath);
}
else if (node instanceof StatusUpstreamNode) {
else if (node instanceof BranchTrackingStatusNode) {
this.sendTerminalCommand('rebase', `-i ${node.status.upstream}`, node.status.repoPath);
}
}

Loading…
Cancel
Save