Skip to content

Commit

Permalink
Adds sorting/filtering to branch/tag methods
Browse files Browse the repository at this point in the history
Consolidates more reusable logic into GitService
  • Loading branch information
eamodio committed Jun 15, 2019
1 parent 0c98110 commit 2892a46
Show file tree
Hide file tree
Showing 20 changed files with 241 additions and 206 deletions.
2 changes: 1 addition & 1 deletion src/annotations/annotations.ts
Expand Up @@ -195,7 +195,7 @@ export class Annotations {
const [presence, previousLineDiffUris, remotes] = await Promise.all([
Container.vsls.getContactPresence(commit.email),
commit.isUncommitted ? commit.getPreviousLineDiffUris(uri, editorLine, uri.sha) : undefined,
Container.git.getRemotes(commit.repoPath)
Container.git.getRemotes(commit.repoPath, { sort: true })
]);

const markdown = new MarkdownString(
Expand Down
4 changes: 1 addition & 3 deletions src/commands/openBranchInRemote.ts
Expand Up @@ -58,9 +58,7 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
{
autoPick: true,
checkmarks: false,
filters: {
branches: b => b.tracking !== undefined
},
filterBranches: b => b.tracking !== undefined,
include: 'branches'
}
);
Expand Down
4 changes: 1 addition & 3 deletions src/commands/openFileInRemote.ts
Expand Up @@ -87,9 +87,7 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
{
autoPick: true,
checkmarks: false,
filters: {
branches: b => b.tracking !== undefined
},
filterBranches: b => b.tracking !== undefined,
include: 'branches'
}
);
Expand Down
6 changes: 0 additions & 6 deletions src/git/formatters/commitFormatter.ts
Expand Up @@ -306,12 +306,6 @@ export class CommitFormatter extends Formatter<GitCommit, CommitFormatOptions> {
}

if (this._options.remotes !== undefined) {
this._options.remotes.sort(
(a, b) =>
(a.default ? -1 : 1) - (b.default ? -1 : 1) ||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })
);

for (const r of this._options.remotes) {
if (r.provider === undefined) continue;

Expand Down
207 changes: 166 additions & 41 deletions src/git/gitService.ts
Expand Up @@ -25,7 +25,7 @@ import { CommandContext, DocumentSchemes, setCommandContext } from '../constants
import { Container } from '../container';
import { LogCorrelationContext, Logger } from '../logger';
import { Messages } from '../messages';
import { gate, Iterables, log, Objects, Strings, TernarySearchTree, Versions } from '../system';
import { Arrays, gate, Iterables, log, Objects, Strings, TernarySearchTree, Versions } from '../system';
import { CachedBlame, CachedDiff, CachedLog, GitDocumentState, TrackedDocument } from '../trackers/gitDocumentTracker';
import { vslsUriPrefixRegex } from '../vsls/vsls';
import {
Expand Down Expand Up @@ -972,32 +972,118 @@ export class GitService implements Disposable {
}

@log()
async getBranches(repoPath: string | undefined): Promise<GitBranch[]> {
async getBranches(
repoPath: string | undefined,
options: { filter?: (b: GitBranch) => boolean; sort?: boolean } = {}
): Promise<GitBranch[]> {
if (repoPath === undefined) return [];

let branches;
if (this.useCaching) {
branches = this._branchesCache.get(repoPath);
if (branches !== undefined) return branches;
let branches: GitBranch[] | undefined;
try {
if (this.useCaching) {
branches = this._branchesCache.get(repoPath);
if (branches !== undefined) return branches;
}

const data = await Git.for_each_ref__branch(repoPath, { all: true });
// If we don't get any data, assume the repo doesn't have any commits yet so check if we have a current branch
if (data == null || data.length === 0) {
const current = await this.getBranch(repoPath);
branches = current !== undefined ? [current] : [];
}
else {
branches = GitBranchParser.parse(data, repoPath);
}

if (this.useCaching) {
const repo = await this.getRepository(repoPath);
if (repo !== undefined && repo.supportsChangeEvents) {
this._branchesCache.set(repoPath, branches);
}
}

return branches;
}
finally {
if (options.filter !== undefined) {
branches = branches!.filter(options.filter);
}

const data = await Git.for_each_ref__branch(repoPath, { all: true });
// If we don't get any data, assume the repo doesn't have any commits yet so check if we have a current branch
if (data == null || data.length === 0) {
const current = await this.getBranch(repoPath);
branches = current !== undefined ? [current] : [];
if (options.sort) {
branches!.sort(
(a, b) =>
(a.starred ? -1 : 1) - (b.starred ? -1 : 1) ||
(b.remote ? -1 : 1) - (a.remote ? -1 : 1) ||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })
);
}

if (options.filter !== undefined) {
// eslint-disable-next-line no-unsafe-finally
return branches!;
}
}
else {
branches = GitBranchParser.parse(data, repoPath);
}

@log()
async getBranchesAndOrTags(
repoPath: string | undefined,
{
filterBranches,
filterTags,
include,
...options
}: {
filterBranches?: (b: GitBranch) => boolean;
filterTags?: (t: GitTag) => boolean;
include?: 'all' | 'branches' | 'tags';
sort?: boolean;
} = {}
) {
const [branches, tags] = await Promise.all<GitBranch[] | undefined, GitTag[] | undefined>([
include === 'all' || include === 'branches'
? Container.git.getBranches(repoPath, {
...options,
filter: filterBranches && filterBranches
})
: undefined,
include === 'all' || include === 'tags'
? Container.git.getTags(repoPath, {
...options,
filter: filterTags && filterTags
})
: undefined
]);

if (branches !== undefined && tags !== undefined) {
return [...branches.filter(b => !b.remote), ...tags, ...branches.filter(b => b.remote)];
}

if (this.useCaching) {
const repo = await this.getRepository(repoPath);
if (repo !== undefined && repo.supportsChangeEvents) {
this._branchesCache.set(repoPath, branches);
}
if (branches !== undefined) {
return branches;
}
return branches;

return tags;
}

@log()
async getBranchesAndTagsTipsFn(repoPath: string | undefined, currentName?: string) {
const [branches, tags] = await Promise.all([
Container.git.getBranches(repoPath),
Container.git.getTags(repoPath, { includeRefs: true })
]);

const branchesAndTagsBySha = Arrays.groupByFilterMap(
(branches as { name: string; sha: string }[]).concat(tags as { name: string; sha: string }[]),
bt => bt.sha!,
bt => (bt.name === currentName ? undefined : bt.name)
);

return (sha: string) => {
const branchesAndTags = branchesAndTagsBySha.get(sha);
if (branchesAndTags === undefined || branchesAndTags.length === 0) return undefined;
return branchesAndTags.join(', ');
};
}

@log()
Expand Down Expand Up @@ -1981,18 +2067,27 @@ export class GitService implements Disposable {
}

@log()
async getRemotes(repoPath: string | undefined, options: { includeAll?: boolean } = {}): Promise<GitRemote[]> {
async getRemotes(
repoPath: string | undefined,
options: { includeAll?: boolean; sort?: boolean } = {}
): Promise<GitRemote[]> {
if (repoPath === undefined) return [];

const repository = await this.getRepository(repoPath);
const remotes = repository !== undefined ? repository.getRemotes() : this.getRemotesCore(repoPath);
const remotes = await (repository !== undefined
? repository.getRemotes({ sort: options.sort })
: this.getRemotesCore(repoPath, undefined, { sort: options.sort }));

if (options.includeAll) return remotes;

return (await remotes).filter(r => r.provider !== undefined);
return remotes.filter(r => r.provider !== undefined);
}

async getRemotesCore(repoPath: string | undefined, providers?: RemoteProviders): Promise<GitRemote[]> {
async getRemotesCore(
repoPath: string | undefined,
providers?: RemoteProviders,
options: { sort?: boolean } = {}
): Promise<GitRemote[]> {
if (repoPath === undefined) return [];

providers =
Expand All @@ -2003,7 +2098,18 @@ export class GitService implements Disposable {

try {
const data = await Git.remote(repoPath);
return GitRemoteParser.parse(data, repoPath, RemoteProviderFactory.factory(providers)) || [];
const remotes = GitRemoteParser.parse(data, repoPath, RemoteProviderFactory.factory(providers));
if (remotes === undefined) return [];

if (options.sort) {
remotes.sort(
(a, b) =>
(a.default ? -1 : 1) - (b.default ? -1 : 1) ||
a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' })
);
}

return remotes;
}
catch (ex) {
Logger.error(ex);
Expand Down Expand Up @@ -2236,37 +2342,56 @@ export class GitService implements Disposable {
}

@log()
async getTags(repoPath: string | undefined, options: { includeRefs?: boolean } = {}): Promise<GitTag[]> {
async getTags(
repoPath: string | undefined,
options: { filter?: (t: GitTag) => boolean; includeRefs?: boolean; sort?: boolean } = {}
): Promise<GitTag[]> {
if (repoPath === undefined) return [];

let tags;
if (options.includeRefs) {
tags = this._tagsWithRefsCache.get(repoPath);
let tags: GitTag[] | undefined;
try {
if (options.includeRefs) {
tags = this._tagsWithRefsCache.get(repoPath);
if (tags !== undefined) return tags;

const data = await Git.show_ref__tags(repoPath);
tags = GitTagParser.parseWithRef(data, repoPath) || [];

const repo = await this.getRepository(repoPath);
if (repo !== undefined && repo.supportsChangeEvents) {
this._tagsWithRefsCache.set(repoPath, tags);
}

return tags;
}

tags = this._tagsCache.get(repoPath);
if (tags !== undefined) return tags;

const data = await Git.show_ref__tags(repoPath);
tags = GitTagParser.parseWithRef(data, repoPath) || [];
const data = await Git.tag(repoPath);
tags = GitTagParser.parse(data, repoPath) || [];

const repo = await this.getRepository(repoPath);
if (repo !== undefined && repo.supportsChangeEvents) {
this._tagsWithRefsCache.set(repoPath, tags);
this._tagsCache.set(repoPath, tags);
}

return tags;
}
finally {
if (options.filter !== undefined) {
tags = tags!.filter(options.filter);
}

tags = this._tagsCache.get(repoPath);
if (tags !== undefined) return tags;

const data = await Git.tag(repoPath);
tags = GitTagParser.parse(data, repoPath) || [];
if (options.sort) {
tags!.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }));
}

const repo = await this.getRepository(repoPath);
if (repo !== undefined && repo.supportsChangeEvents) {
this._tagsCache.set(repoPath, tags);
if (options.filter !== undefined) {
// eslint-disable-next-line no-unsafe-finally
return tags!;
}
}

return tags;
}

@log()
Expand Down
11 changes: 6 additions & 5 deletions src/git/models/repository.ts
Expand Up @@ -263,8 +263,8 @@ export class Repository implements Disposable {
return this._branch;
}

getBranches(): Promise<GitBranch[]> {
return Container.git.getBranches(this.path);
getBranches(options: { filter?: (b: GitBranch) => boolean; sort?: boolean } = {}): Promise<GitBranch[]> {
return Container.git.getBranches(this.path, options);
}

getChangedFilesCount(sha?: string): Promise<GitDiffShortStat | undefined> {
Expand All @@ -284,7 +284,7 @@ export class Repository implements Disposable {
);
}

getRemotes(): Promise<GitRemote[]> {
getRemotes(options: { sort?: boolean } = {}): Promise<GitRemote[]> {
if (this._remotes === undefined || !this.supportsChangeEvents) {
if (this._providers === undefined) {
const remotesCfg = configuration.get<RemotesConfig[] | null | undefined>(
Expand All @@ -294,7 +294,8 @@ export class Repository implements Disposable {
this._providers = RemoteProviderFactory.loadProviders(remotesCfg);
}

this._remotes = Container.git.getRemotesCore(this.path, this._providers);
// Since we are caching the results, always sort
this._remotes = Container.git.getRemotesCore(this.path, this._providers, { sort: true });
}

return this._remotes;
Expand All @@ -308,7 +309,7 @@ export class Repository implements Disposable {
return Container.git.getStatusForRepo(this.path);
}

getTags(options?: { includeRefs?: boolean }): Promise<GitTag[]> {
getTags(options?: { filter?: (t: GitTag) => boolean; includeRefs?: boolean; sort?: boolean }): Promise<GitTag[]> {
return Container.git.getTags(this.path, options);
}

Expand Down
2 changes: 1 addition & 1 deletion src/quickpicks/branchHistoryQuickPick.ts
Expand Up @@ -55,7 +55,7 @@ export class BranchHistoryQuickPick {
[uri, currentCommandArgs]
);

const remotes = await Container.git.getRemotes((uri && uri.repoPath) || log.repoPath);
const remotes = await Container.git.getRemotes((uri && uri.repoPath) || log.repoPath, { sort: true });
if (remotes.length) {
items.splice(
0,
Expand Down
2 changes: 1 addition & 1 deletion src/quickpicks/commitFileQuickPick.ts
Expand Up @@ -191,7 +191,7 @@ export class CommitFileQuickPick {
}
items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit));

const remotes = await Container.git.getRemotes(commit.repoPath);
const remotes = await Container.git.getRemotes(commit.repoPath, { sort: true });
if (remotes.length) {
if (workingUri && commit.status !== 'D') {
const branch = await Container.git.getBranch(commit.repoPath);
Expand Down
2 changes: 1 addition & 1 deletion src/quickpicks/commitQuickPick.ts
Expand Up @@ -293,7 +293,7 @@ export class CommitQuickPick {
else {
items.splice(index++, 0, new ShowCommitInViewQuickPickItem(commit));

remotes = await Container.git.getRemotes(commit.repoPath);
remotes = await Container.git.getRemotes(commit.repoPath, { sort: true });
if (remotes.length) {
items.splice(
index++,
Expand Down
2 changes: 1 addition & 1 deletion src/quickpicks/fileHistoryQuickPick.ts
Expand Up @@ -159,7 +159,7 @@ export class FileHistoryQuickPick {
);
}

const remotes = await Container.git.getRemotes(uri.repoPath!);
const remotes = await Container.git.getRemotes(uri.repoPath!, { sort: true });
if (remotes.length) {
const resource: RemoteResource =
uri.sha !== undefined
Expand Down

0 comments on commit 2892a46

Please sign in to comment.