Просмотр исходного кода

Fixes get previous commit nav for GitHub

main
Eric Amodio 3 лет назад
Родитель
Сommit
d0c8dacfc1
3 измененных файлов: 225 добавлений и 49 удалений
  1. +35
    -35
      src/env/node/git/localGitProvider.ts
  2. +84
    -0
      src/premium/github/github.ts
  3. +106
    -14
      src/premium/github/githubGitProvider.ts

+ 35
- 35
src/env/node/git/localGitProvider.ts Просмотреть файл

@ -2659,14 +2659,14 @@ export class LocalGitProvider implements GitProvider, Disposable {
skip: number = 0, skip: number = 0,
): Promise<{ current: GitUri; next: GitUri | undefined; deleted?: boolean } | undefined> { ): Promise<{ current: GitUri; next: GitUri | undefined; deleted?: boolean } | undefined> {
// If we have no ref (or staged ref) there is no next commit // If we have no ref (or staged ref) there is no next commit
if (ref == null || ref.length === 0) return undefined;
if (!ref) return undefined;
const path = this.getRelativePath(uri, repoPath);
const relativePath = this.getRelativePath(uri, repoPath);
if (GitRevision.isUncommittedStaged(ref)) { if (GitRevision.isUncommittedStaged(ref)) {
return { return {
current: GitUri.fromFile(path, repoPath, ref),
next: GitUri.fromFile(path, repoPath, undefined),
current: GitUri.fromFile(relativePath, repoPath, ref),
next: GitUri.fromFile(relativePath, repoPath, undefined),
}; };
} }
@ -2677,22 +2677,22 @@ export class LocalGitProvider implements GitProvider, Disposable {
// If the file is staged, diff with the staged version // If the file is staged, diff with the staged version
if (status.indexStatus != null) { if (status.indexStatus != null) {
return { return {
current: GitUri.fromFile(path, repoPath, ref),
next: GitUri.fromFile(path, repoPath, GitRevision.uncommittedStaged),
current: GitUri.fromFile(relativePath, repoPath, ref),
next: GitUri.fromFile(relativePath, repoPath, GitRevision.uncommittedStaged),
}; };
} }
} }
return { return {
current: GitUri.fromFile(path, repoPath, ref),
next: GitUri.fromFile(path, repoPath, undefined),
current: GitUri.fromFile(relativePath, repoPath, ref),
next: GitUri.fromFile(relativePath, repoPath, undefined),
}; };
} }
return { return {
current: current:
skip === 0 skip === 0
? GitUri.fromFile(path, repoPath, ref)
? GitUri.fromFile(relativePath, repoPath, ref)
: (await this.getNextUri(repoPath, uri, ref, skip - 1))!, : (await this.getNextUri(repoPath, uri, ref, skip - 1))!,
next: next, next: next,
}; };
@ -2707,7 +2707,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
// editorLine?: number // editorLine?: number
): Promise<GitUri | undefined> { ): Promise<GitUri | undefined> {
// If we have no ref (or staged ref) there is no next commit // If we have no ref (or staged ref) there is no next commit
if (ref == null || ref.length === 0 || GitRevision.isUncommittedStaged(ref)) return undefined;
if (!ref || GitRevision.isUncommittedStaged(ref)) return undefined;
let filters: GitDiffFilter[] | undefined; let filters: GitDiffFilter[] | undefined;
if (ref === GitRevision.deletedOrMissing) { if (ref === GitRevision.deletedOrMissing) {
@ -2764,7 +2764,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
): Promise<{ current: GitUri; previous: GitUri | undefined } | undefined> { ): Promise<{ current: GitUri; previous: GitUri | undefined } | undefined> {
if (ref === GitRevision.deletedOrMissing) return undefined; if (ref === GitRevision.deletedOrMissing) return undefined;
const path = this.getRelativePath(uri, repoPath);
const relativePath = this.getRelativePath(uri, repoPath);
// If we are at the working tree (i.e. no ref), we need to dig deeper to figure out where to go // If we are at the working tree (i.e. no ref), we need to dig deeper to figure out where to go
if (!ref) { if (!ref) {
@ -2782,20 +2782,20 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (skip === 0) { if (skip === 0) {
// Diff working with staged // Diff working with staged
return { return {
current: GitUri.fromFile(path, repoPath, undefined),
previous: GitUri.fromFile(path, repoPath, GitRevision.uncommittedStaged),
current: GitUri.fromFile(relativePath, repoPath, undefined),
previous: GitUri.fromFile(relativePath, repoPath, GitRevision.uncommittedStaged),
}; };
} }
return { return {
// Diff staged with HEAD (or prior if more skips) // Diff staged with HEAD (or prior if more skips)
current: GitUri.fromFile(path, repoPath, GitRevision.uncommittedStaged),
current: GitUri.fromFile(relativePath, repoPath, GitRevision.uncommittedStaged),
previous: await this.getPreviousUri(repoPath, uri, ref, skip - 1, undefined, firstParent), previous: await this.getPreviousUri(repoPath, uri, ref, skip - 1, undefined, firstParent),
}; };
} else if (status.workingTreeStatus != null) { } else if (status.workingTreeStatus != null) {
if (skip === 0) { if (skip === 0) {
return { return {
current: GitUri.fromFile(path, repoPath, undefined),
current: GitUri.fromFile(relativePath, repoPath, undefined),
previous: await this.getPreviousUri(repoPath, uri, undefined, skip, undefined, firstParent), previous: await this.getPreviousUri(repoPath, uri, undefined, skip, undefined, firstParent),
}; };
} }
@ -2808,7 +2808,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
else if (GitRevision.isUncommittedStaged(ref)) { else if (GitRevision.isUncommittedStaged(ref)) {
const current = const current =
skip === 0 skip === 0
? GitUri.fromFile(path, repoPath, ref)
? GitUri.fromFile(relativePath, repoPath, ref)
: (await this.getPreviousUri(repoPath, uri, undefined, skip - 1, undefined, firstParent))!; : (await this.getPreviousUri(repoPath, uri, undefined, skip - 1, undefined, firstParent))!;
if (current == null || current.sha === GitRevision.deletedOrMissing) return undefined; if (current == null || current.sha === GitRevision.deletedOrMissing) return undefined;
@ -2821,7 +2821,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
// If we are at a commit, diff commit with previous // If we are at a commit, diff commit with previous
const current = const current =
skip === 0 skip === 0
? GitUri.fromFile(path, repoPath, ref)
? GitUri.fromFile(relativePath, repoPath, ref)
: (await this.getPreviousUri(repoPath, uri, ref, skip - 1, undefined, firstParent))!; : (await this.getPreviousUri(repoPath, uri, ref, skip - 1, undefined, firstParent))!;
if (current == null || current.sha === GitRevision.deletedOrMissing) return undefined; if (current == null || current.sha === GitRevision.deletedOrMissing) return undefined;
@ -2841,7 +2841,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
): Promise<{ current: GitUri; previous: GitUri | undefined; line: number } | undefined> { ): Promise<{ current: GitUri; previous: GitUri | undefined; line: number } | undefined> {
if (ref === GitRevision.deletedOrMissing) return undefined; if (ref === GitRevision.deletedOrMissing) return undefined;
let path = this.getRelativePath(uri, repoPath);
let relativePath = this.getRelativePath(uri, repoPath);
let previous; let previous;
@ -2868,8 +2868,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (status.indexStatus != null) { if (status.indexStatus != null) {
// Diff working with staged // Diff working with staged
return { return {
current: GitUri.fromFile(path, repoPath, undefined),
previous: GitUri.fromFile(path, repoPath, GitRevision.uncommittedStaged),
current: GitUri.fromFile(relativePath, repoPath, undefined),
previous: GitUri.fromFile(relativePath, repoPath, GitRevision.uncommittedStaged),
line: editorLine, line: editorLine,
}; };
} }
@ -2877,7 +2877,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
// Diff working with HEAD (or prior if more skips) // Diff working with HEAD (or prior if more skips)
return { return {
current: GitUri.fromFile(path, repoPath, undefined),
current: GitUri.fromFile(relativePath, repoPath, undefined),
previous: await this.getPreviousUri(repoPath, uri, undefined, skip, editorLine), previous: await this.getPreviousUri(repoPath, uri, undefined, skip, editorLine),
line: editorLine, line: editorLine,
}; };
@ -2899,19 +2899,19 @@ export class LocalGitProvider implements GitProvider, Disposable {
// If line is committed, diff with line ref with previous // If line is committed, diff with line ref with previous
else { else {
ref = blameLine.commit.sha; ref = blameLine.commit.sha;
path = blameLine.commit.file?.path ?? blameLine.commit.file?.originalPath ?? path;
uri = this.getAbsoluteUri(path, repoPath);
relativePath = blameLine.commit.file?.path ?? blameLine.commit.file?.originalPath ?? relativePath;
uri = this.getAbsoluteUri(relativePath, repoPath);
editorLine = blameLine.line.from.line - 1; editorLine = blameLine.line.from.line - 1;
if (skip === 0 && blameLine.commit.file?.previousSha) { if (skip === 0 && blameLine.commit.file?.previousSha) {
previous = GitUri.fromFile(path, repoPath, blameLine.commit.file.previousSha);
previous = GitUri.fromFile(relativePath, repoPath, blameLine.commit.file.previousSha);
} }
} }
} else { } else {
if (GitRevision.isUncommittedStaged(ref)) { if (GitRevision.isUncommittedStaged(ref)) {
const current = const current =
skip === 0 skip === 0
? GitUri.fromFile(path, repoPath, ref)
? GitUri.fromFile(relativePath, repoPath, ref)
: (await this.getPreviousUri(repoPath, uri, undefined, skip - 1, editorLine))!; : (await this.getPreviousUri(repoPath, uri, undefined, skip - 1, editorLine))!;
if (current.sha === GitRevision.deletedOrMissing) return undefined; if (current.sha === GitRevision.deletedOrMissing) return undefined;
@ -2928,18 +2928,18 @@ export class LocalGitProvider implements GitProvider, Disposable {
// Diff with line ref with previous // Diff with line ref with previous
ref = blameLine.commit.sha; ref = blameLine.commit.sha;
path = blameLine.commit.file?.path ?? blameLine.commit.file?.originalPath ?? path;
uri = this.getAbsoluteUri(path, repoPath);
relativePath = blameLine.commit.file?.path ?? blameLine.commit.file?.originalPath ?? relativePath;
uri = this.getAbsoluteUri(relativePath, repoPath);
editorLine = blameLine.line.from.line - 1; editorLine = blameLine.line.from.line - 1;
if (skip === 0 && blameLine.commit.file?.previousSha) { if (skip === 0 && blameLine.commit.file?.previousSha) {
previous = GitUri.fromFile(path, repoPath, blameLine.commit.file.previousSha);
previous = GitUri.fromFile(relativePath, repoPath, blameLine.commit.file.previousSha);
} }
} }
const current = const current =
skip === 0 skip === 0
? GitUri.fromFile(path, repoPath, ref)
? GitUri.fromFile(relativePath, repoPath, ref)
: (await this.getPreviousUri(repoPath, uri, ref, skip - 1, editorLine))!; : (await this.getPreviousUri(repoPath, uri, ref, skip - 1, editorLine))!;
if (current.sha === GitRevision.deletedOrMissing) return undefined; if (current.sha === GitRevision.deletedOrMissing) return undefined;
@ -2967,12 +2967,12 @@ export class LocalGitProvider implements GitProvider, Disposable {
ref = undefined; ref = undefined;
} }
const path = this.getRelativePath(uri, repoPath);
const relativePath = this.getRelativePath(uri, repoPath);
// TODO: Add caching // TODO: Add caching
let data; let data;
try { try {
data = await this.git.log__file(repoPath, path, ref, {
data = await this.git.log__file(repoPath, relativePath, ref, {
argsOrFormat: GitLogParser.simpleFormat, argsOrFormat: GitLogParser.simpleFormat,
fileMode: 'simple', fileMode: 'simple',
firstParent: firstParent, firstParent: firstParent,
@ -2987,14 +2987,14 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (ref == null) { if (ref == null) {
const status = await this.getStatusForFile(repoPath, uri); const status = await this.getStatusForFile(repoPath, uri);
if (status?.indexStatus != null) { if (status?.indexStatus != null) {
return GitUri.fromFile(path, repoPath, GitRevision.uncommittedStaged);
return GitUri.fromFile(relativePath, repoPath, GitRevision.uncommittedStaged);
} }
} }
ref = await this.git.log__file_recent(repoPath, path, {
ref = await this.git.log__file_recent(repoPath, relativePath, {
ordering: this.container.config.advanced.commitOrdering, ordering: this.container.config.advanced.commitOrdering,
}); });
return GitUri.fromFile(path, repoPath, ref ?? GitRevision.deletedOrMissing);
return GitUri.fromFile(relativePath, repoPath, ref ?? GitRevision.deletedOrMissing);
} }
Logger.error(ex, cc); Logger.error(ex, cc);
@ -3006,7 +3006,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
// If the previous ref matches the ref we asked for assume we are at the end of the history // If the previous ref matches the ref we asked for assume we are at the end of the history
if (ref != null && ref === previousRef) return undefined; if (ref != null && ref === previousRef) return undefined;
return GitUri.fromFile(file ?? path, repoPath, previousRef ?? GitRevision.deletedOrMissing);
return GitUri.fromFile(file ?? relativePath, repoPath, previousRef ?? GitRevision.deletedOrMissing);
} }
@log() @log()

+ 84
- 0
src/premium/github/github.ts Просмотреть файл

@ -2,6 +2,7 @@ import { Octokit } from '@octokit/core';
import { GraphqlResponseError } from '@octokit/graphql'; import { GraphqlResponseError } from '@octokit/graphql';
import { RequestError } from '@octokit/request-error'; import { RequestError } from '@octokit/request-error';
import type { Endpoints, OctokitResponse, RequestParameters } from '@octokit/types'; import type { Endpoints, OctokitResponse, RequestParameters } from '@octokit/types';
import { window } from 'vscode';
import { fetch } from '@env/fetch'; import { fetch } from '@env/fetch';
import { isWeb } from '@env/platform'; import { isWeb } from '@env/platform';
import { import {
@ -14,6 +15,7 @@ import { PagedResult } from '../../git/gitProvider';
import { import {
type DefaultBranch, type DefaultBranch,
GitFileIndexStatus, GitFileIndexStatus,
GitRevision,
type GitUser, type GitUser,
type IssueOrPullRequest, type IssueOrPullRequest,
type IssueOrPullRequestType, type IssueOrPullRequestType,
@ -700,6 +702,9 @@ export class GitHubApi {
ref: string, ref: string,
path: string, path: string,
): Promise<(GitHubCommit & { viewer?: string }) | undefined> { ): Promise<(GitHubCommit & { viewer?: string }) | undefined> {
if (GitRevision.isSha(ref)) return this.getCommit(token, owner, repo, ref);
// TODO: optimize this -- only need to get the sha for the ref
const results = await this.getCommits(token, owner, repo, ref, { limit: 1, path: path }); const results = await this.getCommits(token, owner, repo, ref, { limit: 1, path: path });
if (results.values.length === 0) return undefined; if (results.values.length === 0) return undefined;
@ -1079,6 +1084,83 @@ export class GitHubApi {
} }
} }
@debug<GitHubApi['getCommitRefs']>({ args: { 0: '<token>' } })
async getCommitRefs(
token: string,
owner: string,
repo: string,
ref: string,
options?: {
after?: string;
before?: string;
limit?: number;
path?: string;
since?: Date;
until?: Date;
},
): Promise<string[]> {
const cc = Logger.getCorrelationContext();
interface QueryResult {
repository:
| {
object:
| {
history: {
nodes: { oid: string }[];
};
}
| null
| undefined;
}
| null
| undefined;
}
try {
const query = `query getCommitRefs(
$owner: String!
$repo: String!
$ref: String!
$after: String
$before: String
$limit: Int = 1
$path: String
$since: GitTimestamp
$until: GitTimestamp
) {
repository(name: $repo, owner: $owner) {
object(expression: $ref) {
... on Commit {
history(first: $limit, path: $path, since: $since, until: $until, after: $after, before: $before) {
nodes { oid }
}
}
}
}
}`;
const rsp = await this.graphql<QueryResult>(token, query, {
owner: owner,
repo: repo,
ref: ref,
path: options?.path,
limit: Math.max(1, options?.limit ?? 1),
after: options?.after,
before: options?.before,
since: options?.since?.toISOString(),
until: options?.until?.toISOString(),
});
const history = rsp?.repository?.object?.history;
if (history == null) return [];
return history.nodes.map(n => n.oid);
} catch (ex) {
debugger;
return this.handleRequestError<string[]>(ex, cc, []);
}
}
@debug<GitHubApi['getContributors']>({ args: { 0: '<token>' } }) @debug<GitHubApi['getContributors']>({ args: { 0: '<token>' } })
async getContributors(token: string, owner: string, repo: string): Promise<GitHubContributor[]> { async getContributors(token: string, owner: string, repo: string): Promise<GitHubContributor[]> {
const cc = Logger.getCorrelationContext(); const cc = Logger.getCorrelationContext();
@ -1410,6 +1492,7 @@ export class GitHubApi {
} }
} }
void window.showErrorMessage(`Unable to complete GitHub request: ${ex.message}`);
throw ex; throw ex;
} }
} }
@ -1444,6 +1527,7 @@ export class GitHubApi {
} }
} }
void window.showErrorMessage(`Unable to complete GitHub request: ${ex.message}`);
throw ex; throw ex;
} }
} }

+ 106
- 14
src/premium/github/githubGitProvider.ts Просмотреть файл

@ -1254,6 +1254,7 @@ export class GitHubGitProvider implements GitProvider, Disposable {
_search: SearchPattern, _search: SearchPattern,
_options?: { limit?: number; ordering?: string | null; skip?: number }, _options?: { limit?: number; ordering?: string | null; skip?: number },
): Promise<GitLog | undefined> { ): Promise<GitLog | undefined> {
// TODO@eamodio try implementing with the commit search api
return undefined; return undefined;
} }
@ -1640,23 +1641,71 @@ export class GitHubGitProvider implements GitProvider, Disposable {
@log() @log()
async getNextDiffUris( async getNextDiffUris(
_repoPath: string,
_uri: Uri,
_ref: string | undefined,
_skip: number = 0,
repoPath: string,
uri: Uri,
ref: string | undefined,
skip: number = 0,
): Promise<{ current: GitUri; next: GitUri | undefined; deleted?: boolean } | undefined> { ): Promise<{ current: GitUri; next: GitUri | undefined; deleted?: boolean } | undefined> {
return undefined;
// If we have no ref (or staged ref) there is no next commit
if (!ref) return undefined;
const relativePath = this.getRelativePath(uri, repoPath);
return {
current:
skip === 0
? GitUri.fromFile(relativePath, repoPath, ref)
: (await this.getNextUri(repoPath, uri, ref, skip - 1))!,
next: await this.getNextUri(repoPath, uri, ref, skip),
};
} }
@log() @log()
async getNextUri( async getNextUri(
_repoPath: string, _repoPath: string,
_uri: Uri, _uri: Uri,
_ref?: string,
ref?: string,
_skip: number = 0, _skip: number = 0,
// editorLine?: number // editorLine?: number
): Promise<GitUri | undefined> { ): Promise<GitUri | undefined> {
// If we have no ref (or staged ref) there is no next commit
if (!ref || GitRevision.isUncommittedStaged(ref)) return undefined;
return undefined; return undefined;
// const cc = Logger.getCorrelationContext();
// // const relativePath = this.getRelativePath(uri, repoPath);
// try {
// const context = await this.ensureRepositoryContext(repoPath);
// if (context == null) return undefined;
// const { metadata, github, remotehub, session } = context;
// const relativePath = this.getRelativePath(uri, remotehub.getProviderRootUri(uri));
// const refs = await github.getCommitRefs(
// session.accessToken,
// metadata.repo.owner,
// metadata.repo.name,
// ref ?? 'HEAD',
// {
// path: relativePath,
// limit: skip + 2,
// before: `${ref} 0`,
// },
// );
// const nextRef = refs[skip];
// if (nextRef == null) return undefined;
// const next = await this.getBestRevisionUri(repoPath, relativePath, nextRef);
// return new GitUri(next);
// } catch (ex) {
// Logger.error(ex, cc);
// debugger;
// throw ex;
// }
} }
@log() @log()
@ -1664,17 +1713,24 @@ export class GitHubGitProvider implements GitProvider, Disposable {
repoPath: string, repoPath: string,
uri: Uri, uri: Uri,
ref: string | undefined, ref: string | undefined,
_skip: number = 0,
skip: number = 0,
firstParent: boolean = false, firstParent: boolean = false,
): Promise<{ current: GitUri; previous: GitUri | undefined } | undefined> { ): Promise<{ current: GitUri; previous: GitUri | undefined } | undefined> {
if (ref === GitRevision.deletedOrMissing) return undefined; if (ref === GitRevision.deletedOrMissing) return undefined;
const path = this.getRelativePath(uri, repoPath);
const relativePath = this.getRelativePath(uri, repoPath);
// If we are at a commit, diff commit with previous
const current =
skip === 0
? GitUri.fromFile(relativePath, repoPath, ref)
: (await this.getPreviousUri(repoPath, uri, ref, skip - 1, undefined, firstParent))!;
if (current == null || current.sha === GitRevision.deletedOrMissing) return undefined;
return { return {
current: GitUri.fromFile(path, repoPath, undefined),
previous: await this.getPreviousUri(repoPath, uri, ref, 0, undefined, firstParent),
current: current,
previous: await this.getPreviousUri(repoPath, uri, ref, skip, undefined, firstParent),
}; };
// return undefined;
} }
@log() @log()
@ -1693,14 +1749,50 @@ export class GitHubGitProvider implements GitProvider, Disposable {
repoPath: string, repoPath: string,
uri: Uri, uri: Uri,
ref?: string, ref?: string,
_skip: number = 0,
skip: number = 0,
_editorLine?: number, _editorLine?: number,
_firstParent: boolean = false, _firstParent: boolean = false,
): Promise<GitUri | undefined> { ): Promise<GitUri | undefined> {
if (ref === GitRevision.deletedOrMissing) return undefined; if (ref === GitRevision.deletedOrMissing) return undefined;
const commit = await this.getCommitForFile(repoPath, uri, { ref: `${ref ?? 'HEAD'}^` });
return commit?.getGitUri();
const cc = Logger.getCorrelationContext();
if (ref === GitRevision.uncommitted) {
ref = undefined;
}
try {
const context = await this.ensureRepositoryContext(repoPath);
if (context == null) return undefined;
const { metadata, github, remotehub, session } = context;
const relativePath = this.getRelativePath(uri, remotehub.getProviderRootUri(uri));
const offset = ref != null ? 1 : 0;
const refs = await github.getCommitRefs(
session.accessToken,
metadata.repo.owner,
metadata.repo.name,
ref ?? 'HEAD',
{
path: relativePath,
limit: offset + skip + 1,
},
);
const previous = await this.getBestRevisionUri(
repoPath,
relativePath,
refs[offset + skip] ?? GitRevision.deletedOrMissing,
);
return new GitUri(previous);
} catch (ex) {
Logger.error(ex, cc);
debugger;
throw ex;
}
} }
@log() @log()

Загрузка…
Отмена
Сохранить