Browse Source

Improve comparison deep link processing from PRs (#2894)

* Adds secondary remote match and improves branch resolution

* Updates branch format to {owner}/{repoName}:{branchName}
main
Ramin Tadayon 1 year ago
committed by GitHub
parent
commit
efd7ba047e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 211 additions and 104 deletions
  1. +14
    -9
      src/uris/deepLinks/deepLink.ts
  2. +197
    -95
      src/uris/deepLinks/deepLinkService.ts

+ 14
- 9
src/uris/deepLinks/deepLink.ts View File

@ -53,6 +53,7 @@ export interface DeepLink {
repoPath?: string; repoPath?: string;
targetId?: string; targetId?: string;
secondaryTargetId?: string; secondaryTargetId?: string;
secondaryRemoteUrl?: string;
} }
export function parseDeepLinkUri(uri: Uri): DeepLink | undefined { export function parseDeepLinkUri(uri: Uri): DeepLink | undefined {
@ -86,6 +87,7 @@ export function parseDeepLinkUri(uri: Uri): DeepLink | undefined {
let targetId: string; let targetId: string;
let secondaryTargetId: string | undefined; let secondaryTargetId: string | undefined;
let secondaryRemoteUrl: string | undefined;
const joined = rest.join('/'); const joined = rest.join('/');
if (target === DeepLinkType.Comparison) { if (target === DeepLinkType.Comparison) {
@ -93,6 +95,10 @@ export function parseDeepLinkUri(uri: Uri): DeepLink | undefined {
if (split.length !== 3) return undefined; if (split.length !== 3) return undefined;
targetId = split[0]; targetId = split[0];
secondaryTargetId = split[2]; secondaryTargetId = split[2];
secondaryRemoteUrl = urlParams.get('prRepoUrl') ?? undefined;
if (secondaryRemoteUrl != null) {
secondaryRemoteUrl = decodeURIComponent(secondaryRemoteUrl);
}
} else { } else {
targetId = joined; targetId = joined;
} }
@ -104,6 +110,7 @@ export function parseDeepLinkUri(uri: Uri): DeepLink | undefined {
repoPath: repoPath, repoPath: repoPath,
targetId: targetId, targetId: targetId,
secondaryTargetId: secondaryTargetId, secondaryTargetId: secondaryTargetId,
secondaryRemoteUrl: secondaryRemoteUrl,
}; };
} }
@ -129,15 +136,14 @@ export const enum DeepLinkServiceAction {
DeepLinkStored, DeepLinkStored,
DeepLinkErrored, DeepLinkErrored,
OpenRepo, OpenRepo,
RepoMatched,
RepoMatchedInLocalMapping, RepoMatchedInLocalMapping,
RepoMatchedWithId,
RepoMatchedWithPath,
RepoMatchedWithRemoteUrl,
RepoMatchFailed, RepoMatchFailed,
RepoAdded, RepoAdded,
RepoOpened, RepoOpened,
RemoteMatched, RemoteMatched,
RemoteMatchFailed, RemoteMatchFailed,
RemoteMatchUnneeded,
RemoteAdded, RemoteAdded,
TargetMatched, TargetMatched,
TargetsMatched, TargetsMatched,
@ -159,9 +165,11 @@ export interface DeepLinkServiceContext {
repo?: Repository | undefined; repo?: Repository | undefined;
remoteUrl?: string | undefined; remoteUrl?: string | undefined;
remote?: GitRemote | undefined; remote?: GitRemote | undefined;
secondaryRemote?: GitRemote | undefined;
repoPath?: string | undefined; repoPath?: string | undefined;
targetId?: string | undefined; targetId?: string | undefined;
secondaryTargetId?: string | undefined; secondaryTargetId?: string | undefined;
secondaryRemoteUrl?: string | undefined;
targetType?: DeepLinkType | undefined; targetType?: DeepLinkType | undefined;
targetSha?: string | undefined; targetSha?: string | undefined;
secondaryTargetSha?: string | undefined; secondaryTargetSha?: string | undefined;
@ -173,9 +181,7 @@ export const deepLinkStateTransitionTable: Record
}, },
[DeepLinkServiceState.RepoMatch]: { [DeepLinkServiceState.RepoMatch]: {
[DeepLinkServiceAction.DeepLinkErrored]: DeepLinkServiceState.Idle, [DeepLinkServiceAction.DeepLinkErrored]: DeepLinkServiceState.Idle,
[DeepLinkServiceAction.RepoMatchedWithId]: DeepLinkServiceState.RemoteMatch,
[DeepLinkServiceAction.RepoMatchedWithPath]: DeepLinkServiceState.TargetMatch,
[DeepLinkServiceAction.RepoMatchedWithRemoteUrl]: DeepLinkServiceState.TargetMatch,
[DeepLinkServiceAction.RepoMatched]: DeepLinkServiceState.RemoteMatch,
[DeepLinkServiceAction.RepoMatchedInLocalMapping]: DeepLinkServiceState.CloneOrAddRepo, [DeepLinkServiceAction.RepoMatchedInLocalMapping]: DeepLinkServiceState.CloneOrAddRepo,
[DeepLinkServiceAction.RepoMatchFailed]: DeepLinkServiceState.CloneOrAddRepo, [DeepLinkServiceAction.RepoMatchFailed]: DeepLinkServiceState.CloneOrAddRepo,
}, },
@ -192,15 +198,14 @@ export const deepLinkStateTransitionTable: Record
[DeepLinkServiceAction.DeepLinkCancelled]: DeepLinkServiceState.Idle, [DeepLinkServiceAction.DeepLinkCancelled]: DeepLinkServiceState.Idle,
}, },
[DeepLinkServiceState.AddedRepoMatch]: { [DeepLinkServiceState.AddedRepoMatch]: {
[DeepLinkServiceAction.RepoMatchedWithId]: DeepLinkServiceState.RemoteMatch,
[DeepLinkServiceAction.RepoMatchedWithPath]: DeepLinkServiceState.TargetMatch,
[DeepLinkServiceAction.RepoMatchedWithRemoteUrl]: DeepLinkServiceState.TargetMatch,
[DeepLinkServiceAction.RepoMatched]: DeepLinkServiceState.RemoteMatch,
[DeepLinkServiceAction.DeepLinkErrored]: DeepLinkServiceState.Idle, [DeepLinkServiceAction.DeepLinkErrored]: DeepLinkServiceState.Idle,
}, },
[DeepLinkServiceState.RemoteMatch]: { [DeepLinkServiceState.RemoteMatch]: {
[DeepLinkServiceAction.DeepLinkErrored]: DeepLinkServiceState.Idle, [DeepLinkServiceAction.DeepLinkErrored]: DeepLinkServiceState.Idle,
[DeepLinkServiceAction.RemoteMatched]: DeepLinkServiceState.TargetMatch, [DeepLinkServiceAction.RemoteMatched]: DeepLinkServiceState.TargetMatch,
[DeepLinkServiceAction.RemoteMatchFailed]: DeepLinkServiceState.AddRemote, [DeepLinkServiceAction.RemoteMatchFailed]: DeepLinkServiceState.AddRemote,
[DeepLinkServiceAction.RemoteMatchUnneeded]: DeepLinkServiceState.TargetMatch,
}, },
[DeepLinkServiceState.AddRemote]: { [DeepLinkServiceState.AddRemote]: {
[DeepLinkServiceAction.RemoteAdded]: DeepLinkServiceState.TargetMatch, [DeepLinkServiceAction.RemoteAdded]: DeepLinkServiceState.TargetMatch,

+ 197
- 95
src/uris/deepLinks/deepLinkService.ts View File

@ -5,8 +5,6 @@ import type { Container } from '../../container';
import { getBranchNameWithoutRemote } from '../../git/models/branch'; import { getBranchNameWithoutRemote } from '../../git/models/branch';
import type { GitReference } from '../../git/models/reference'; import type { GitReference } from '../../git/models/reference';
import { createReference, isSha } from '../../git/models/reference'; import { createReference, isSha } from '../../git/models/reference';
import type { GitRemote } from '../../git/models/remote';
import type { Repository } from '../../git/models/repository';
import { parseGitRemoteUrl } from '../../git/parsers/remoteParser'; import { parseGitRemoteUrl } from '../../git/parsers/remoteParser';
import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol'; import type { ShowInCommitGraphCommandArgs } from '../../plus/webviews/graph/protocol';
import { executeCommand } from '../../system/command'; import { executeCommand } from '../../system/command';
@ -99,9 +97,11 @@ export class DeepLinkService implements Disposable {
repo: undefined, repo: undefined,
remoteUrl: undefined, remoteUrl: undefined,
remote: undefined, remote: undefined,
secondaryRemote: undefined,
repoPath: undefined, repoPath: undefined,
targetId: undefined, targetId: undefined,
secondaryTargetId: undefined, secondaryTargetId: undefined,
secondaryRemoteUrl: undefined,
targetType: undefined, targetType: undefined,
targetSha: undefined, targetSha: undefined,
}; };
@ -117,6 +117,7 @@ export class DeepLinkService implements Disposable {
repoPath: link.repoPath, repoPath: link.repoPath,
targetId: link.targetId, targetId: link.targetId,
secondaryTargetId: link.secondaryTargetId, secondaryTargetId: link.secondaryTargetId,
secondaryRemoteUrl: link.secondaryRemoteUrl,
}; };
} }
@ -152,17 +153,44 @@ export class DeepLinkService implements Disposable {
} }
private async getShaForBranch(targetId: string): Promise<string | undefined> { private async getShaForBranch(targetId: string): Promise<string | undefined> {
const { repo, remote } = this._context;
const { repo, remote, secondaryRemote } = this._context;
if (!repo) return undefined; if (!repo) return undefined;
// Form the target branch name using the remote name and branch name
const branchName = remote != null ? `${remote.name}/${targetId}` : targetId;
let branchName: string = targetId;
// If the branch name doesn't start with a remote name, first try using the primary and secondary remotes
if (remote != null && !branchName.startsWith(`${remote.name}/`)) {
branchName = `${remote.name}/${branchName}`;
} else if (secondaryRemote != null && !branchName.startsWith(`${secondaryRemote.name}/`)) {
branchName = `${secondaryRemote.name}/${branchName}`;
}
let branch = await repo.getBranch(branchName); let branch = await repo.getBranch(branchName);
if (branch?.sha != null) { if (branch?.sha != null) {
return branch.sha; return branch.sha;
} }
// If it doesn't exist on the target remote, it may still exist locally.
// If that fails, try matching to any existing remote using its path.
if (targetId.includes(':')) {
const [providerRepoInfo, branchBaseName] = targetId.split(':');
if (providerRepoInfo != null && branchName != null) {
const [owner, repoName] = providerRepoInfo.split('/');
if (owner != null && repoName != null) {
const remotes = await repo.getRemotes();
for (const remote of remotes) {
if (remote.provider?.owner === owner) {
branchName = `${remote.name}/${branchBaseName}`;
branch = await repo.getBranch(branchName);
if (branch?.sha != null) {
return branch.sha;
}
}
}
}
}
}
// If the above don't work, it may still exist locally.
branch = await repo.getBranch(targetId); branch = await repo.getBranch(targetId);
if (branch?.sha != null) { if (branch?.sha != null) {
return branch.sha; return branch.sha;
@ -192,36 +220,35 @@ export class DeepLinkService implements Disposable {
return undefined; return undefined;
} }
private async getRefsForComparison(
private async getShasForComparison(
targetId: string, targetId: string,
secondaryTargetId: string, secondaryTargetId: string,
): Promise<[string, string] | undefined> { ): Promise<[string, string] | undefined> {
const ref1 = (await this.validateComparisonRef(targetId)) ? targetId : undefined;
if (ref1 == null) return undefined;
const ref2 = (await this.validateComparisonRef(secondaryTargetId)) ? secondaryTargetId : undefined;
if (ref2 == null) return undefined;
return [ref1, ref2];
const sha1 = await this.getComparisonRefSha(targetId);
if (sha1 == null) return undefined;
const sha2 = await this.getComparisonRefSha(secondaryTargetId);
if (sha2 == null) return undefined;
return [sha1, sha2];
} }
private async validateComparisonRef(ref: string) {
private async getComparisonRefSha(ref: string) {
// try treating each id as a commit sha first, then a branch if that fails, then a tag if that fails. // try treating each id as a commit sha first, then a branch if that fails, then a tag if that fails.
// Note: a blank target id will be treated as 'Working Tree' and will resolve to a blank Sha. // Note: a blank target id will be treated as 'Working Tree' and will resolve to a blank Sha.
if (ref === '') return true;
if (ref === '') return ref;
if (isSha(ref)) return (await this.getShaForCommit(ref)) != null;
if (isSha(ref)) return this.getShaForCommit(ref);
const normalized = ref.toLocaleLowerCase(); const normalized = ref.toLocaleLowerCase();
if (!normalized.startsWith('refs/tags/') && !normalized.startsWith('tags/')) { if (!normalized.startsWith('refs/tags/') && !normalized.startsWith('tags/')) {
const branch = await this.getShaForBranch(ref);
if (branch != null) return true;
const branchSha = await this.getShaForBranch(ref);
if (branchSha != null) return branchSha;
} }
const tag = await this.getShaForTag(ref);
if (tag != null) return true;
const tagSha = await this.getShaForTag(ref);
if (tagSha != null) return tagSha;
const sha = await this.getShaForCommit(ref);
return sha != null;
return this.getShaForCommit(ref);
} }
private async getShasForTargets(): Promise<string | string[] | undefined> { private async getShasForTargets(): Promise<string | string[] | undefined> {
@ -241,7 +268,7 @@ export class DeepLinkService implements Disposable {
if (targetType === DeepLinkType.Comparison) { if (targetType === DeepLinkType.Comparison) {
if (secondaryTargetId == null) return undefined; if (secondaryTargetId == null) return undefined;
return this.getRefsForComparison(targetId, secondaryTargetId);
return this.getShasForComparison(targetId, secondaryTargetId);
} }
return undefined; return undefined;
@ -339,20 +366,6 @@ export class DeepLinkService implements Disposable {
//Repo match //Repo match
let matchingLocalRepoPaths: string[] = []; let matchingLocalRepoPaths: string[] = [];
// Remote match
let matchingRemotes: GitRemote[] = [];
let remoteDomain = '';
let remotePath = '';
let remoteName = undefined;
// Repo open/clone
let repoOpenType;
let repoOpenLocation;
let repoOpenUri: Uri | undefined = undefined;
let repoClonePath: string | undefined = undefined;
let chosenRepo: Repository | undefined = undefined;
let chosenRepoPath;
queueMicrotask( queueMicrotask(
() => () =>
void window.withProgress( void window.withProgress(
@ -384,11 +397,25 @@ export class DeepLinkService implements Disposable {
while (true) { while (true) {
this._context.state = deepLinkStateTransitionTable[this._context.state][action]; this._context.state = deepLinkStateTransitionTable[this._context.state][action];
const { state, repoId, repo, url, remoteUrl, remote, repoPath, targetSha, secondaryTargetSha, targetType } =
this._context;
const {
state,
repoId,
repo,
url,
remoteUrl,
secondaryRemoteUrl,
remote,
secondaryRemote,
repoPath,
targetId,
secondaryTargetId,
targetSha,
secondaryTargetSha,
targetType,
} = this._context;
this._onDeepLinkProgressUpdated.fire(deepLinkStateToProgress[state]); this._onDeepLinkProgressUpdated.fire(deepLinkStateToProgress[state]);
switch (state) { switch (state) {
case DeepLinkServiceState.Idle:
case DeepLinkServiceState.Idle: {
if (action === DeepLinkServiceAction.DeepLinkErrored) { if (action === DeepLinkServiceAction.DeepLinkErrored) {
void window.showErrorMessage('Unable to resolve link'); void window.showErrorMessage('Unable to resolve link');
Logger.warn(`Unable to resolve link - ${message}: ${url}`); Logger.warn(`Unable to resolve link - ${message}: ${url}`);
@ -397,14 +424,17 @@ export class DeepLinkService implements Disposable {
// Deep link processing complete. Reset the context and return. // Deep link processing complete. Reset the context and return.
this.resetContext(); this.resetContext();
return; return;
}
case DeepLinkServiceState.RepoMatch: case DeepLinkServiceState.RepoMatch:
case DeepLinkServiceState.AddedRepoMatch:
case DeepLinkServiceState.AddedRepoMatch: {
if (!repoId && !remoteUrl && !repoPath) { if (!repoId && !remoteUrl && !repoPath) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'No repository id, remote url or path was provided.'; message = 'No repository id, remote url or path was provided.';
break; break;
} }
let remoteDomain = '';
let remotePath = '';
if (remoteUrl != null) { if (remoteUrl != null) {
[, remoteDomain, remotePath] = parseGitRemoteUrl(remoteUrl); [, remoteDomain, remotePath] = parseGitRemoteUrl(remoteUrl);
} }
@ -416,19 +446,18 @@ export class DeepLinkService implements Disposable {
normalizePath(repo.path.toLowerCase()) === normalizePath(repoPath.toLowerCase()) normalizePath(repo.path.toLowerCase()) === normalizePath(repoPath.toLowerCase())
) { ) {
this._context.repo = repo; this._context.repo = repo;
action = DeepLinkServiceAction.RepoMatchedWithPath;
action = DeepLinkServiceAction.RepoMatched;
break; break;
} }
if (remoteDomain != null && remotePath != null) { if (remoteDomain != null && remotePath != null) {
matchingRemotes = await repo.getRemotes({
// eslint-disable-next-line no-loop-func
const matchingRemotes = await repo.getRemotes({
filter: r => r.matches(remoteDomain, remotePath), filter: r => r.matches(remoteDomain, remotePath),
}); });
if (matchingRemotes.length > 0) { if (matchingRemotes.length > 0) {
this._context.repo = repo; this._context.repo = repo;
this._context.remote = matchingRemotes[0]; this._context.remote = matchingRemotes[0];
action = DeepLinkServiceAction.RepoMatchedWithRemoteUrl;
action = DeepLinkServiceAction.RepoMatched;
break; break;
} }
} }
@ -438,13 +467,13 @@ export class DeepLinkService implements Disposable {
// first commit SHA. // first commit SHA.
if (await this.container.git.validateReference(repo.path, repoId)) { if (await this.container.git.validateReference(repo.path, repoId)) {
this._context.repo = repo; this._context.repo = repo;
action = DeepLinkServiceAction.RepoMatchedWithId;
action = DeepLinkServiceAction.RepoMatched;
break; break;
} }
} }
} }
if (!this._context.repo) {
if (!this._context.repo && state === DeepLinkServiceState.RepoMatch) {
matchingLocalRepoPaths = await this.container.repositoryPathMapping.getLocalRepoPaths({ matchingLocalRepoPaths = await this.container.repositoryPathMapping.getLocalRepoPaths({
remoteUrl: remoteUrl, remoteUrl: remoteUrl,
}); });
@ -464,14 +493,18 @@ export class DeepLinkService implements Disposable {
} }
break; break;
case DeepLinkServiceState.CloneOrAddRepo:
}
case DeepLinkServiceState.CloneOrAddRepo: {
if (!repoId && !remoteUrl && !repoPath) { if (!repoId && !remoteUrl && !repoPath) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing repository id, remote url and path.'; message = 'Missing repository id, remote url and path.';
break; break;
} }
let chosenRepoPath: string | undefined;
let repoOpenType: DeepLinkRepoOpenType | undefined;
let repoOpenUri: Uri | undefined;
if (matchingLocalRepoPaths.length > 0) { if (matchingLocalRepoPaths.length > 0) {
chosenRepoPath = await window.showQuickPick( chosenRepoPath = await window.showQuickPick(
[...matchingLocalRepoPaths, 'Choose a different location'], [...matchingLocalRepoPaths, 'Choose a different location'],
@ -501,7 +534,7 @@ export class DeepLinkService implements Disposable {
break; break;
} }
repoOpenLocation = await this.showOpenLocationPrompt(repoOpenType);
const repoOpenLocation = await this.showOpenLocationPrompt(repoOpenType);
if (!repoOpenLocation) { if (!repoOpenLocation) {
action = DeepLinkServiceAction.DeepLinkCancelled; action = DeepLinkServiceAction.DeepLinkCancelled;
break; break;
@ -534,13 +567,14 @@ export class DeepLinkService implements Disposable {
if (repoOpenUri != null && remoteUrl != null && repoOpenType === DeepLinkRepoOpenType.Clone) { if (repoOpenUri != null && remoteUrl != null && repoOpenType === DeepLinkRepoOpenType.Clone) {
// clone the repository, then set repoOpenUri to the repo path // clone the repository, then set repoOpenUri to the repo path
let repoClonePath;
try { try {
repoClonePath = await window.withProgress( repoClonePath = await window.withProgress(
{ {
location: ProgressLocation.Notification, location: ProgressLocation.Notification,
title: `Cloning repository for link: ${this._context.url}}`, title: `Cloning repository for link: ${this._context.url}}`,
}, },
// eslint-disable-next-line no-loop-func
async () => this.container.git.clone(remoteUrl, repoOpenUri?.fsPath ?? ''), async () => this.container.git.clone(remoteUrl, repoOpenUri?.fsPath ?? ''),
); );
} catch { } catch {
@ -564,7 +598,7 @@ export class DeepLinkService implements Disposable {
repoOpenType !== DeepLinkRepoOpenType.Workspace && repoOpenType !== DeepLinkRepoOpenType.Workspace &&
!matchingLocalRepoPaths.includes(repoOpenUri.fsPath) !matchingLocalRepoPaths.includes(repoOpenUri.fsPath)
) { ) {
chosenRepo = await this.container.git.getOrOpenRepository(repoOpenUri, {
const chosenRepo = await this.container.git.getOrOpenRepository(repoOpenUri, {
closeOnOpen: true, closeOnOpen: true,
detectNested: false, detectNested: false,
}); });
@ -592,72 +626,128 @@ export class DeepLinkService implements Disposable {
openWorkspace(repoOpenUri, { location: repoOpenLocation }); openWorkspace(repoOpenUri, { location: repoOpenLocation });
break; break;
case DeepLinkServiceState.OpeningRepo:
}
case DeepLinkServiceState.OpeningRepo: {
this._disposables.push( this._disposables.push(
once(this.container.git.onDidChangeRepositories)(() => { once(this.container.git.onDidChangeRepositories)(() => {
queueMicrotask(() => this.processDeepLink(DeepLinkServiceAction.RepoAdded)); queueMicrotask(() => this.processDeepLink(DeepLinkServiceAction.RepoAdded));
}), }),
); );
return; return;
}
case DeepLinkServiceState.RemoteMatch: {
if (repoPath && repo && !remoteUrl && !secondaryRemoteUrl) {
action = DeepLinkServiceAction.RemoteMatchUnneeded;
break;
}
case DeepLinkServiceState.RemoteMatch:
if (!repo || !remoteUrl) {
if (!repo || (!remoteUrl && !secondaryRemoteUrl)) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing repository or remote url.'; message = 'Missing repository or remote url.';
break; break;
} }
matchingRemotes = await repo.getRemotes({ filter: r => r.url === remoteUrl });
if (matchingRemotes.length > 0) {
this._context.remote = matchingRemotes[0];
action = DeepLinkServiceAction.RemoteMatched;
break;
if (remoteUrl && !remote) {
const matchingRemotes = await repo.getRemotes({ filter: r => r.url === remoteUrl });
if (matchingRemotes.length > 0) {
this._context.remote = matchingRemotes[0];
}
} }
if (!this._context.remote) {
if (secondaryRemoteUrl && !secondaryRemote) {
const matchingRemotes = await repo.getRemotes({ filter: r => r.url === secondaryRemoteUrl });
if (matchingRemotes.length > 0) {
this._context.secondaryRemote = matchingRemotes[0];
}
}
if (
(remoteUrl && !this._context.remote) ||
(secondaryRemoteUrl && !this._context.secondaryRemote)
) {
action = DeepLinkServiceAction.RemoteMatchFailed; action = DeepLinkServiceAction.RemoteMatchFailed;
} else {
action = DeepLinkServiceAction.RemoteMatched;
} }
break; break;
case DeepLinkServiceState.AddRemote:
if (!repo || !remoteUrl) {
}
case DeepLinkServiceState.AddRemote: {
if (!repo || (!remoteUrl && !secondaryRemoteUrl)) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing repository or remote url.'; message = 'Missing repository or remote url.';
break; break;
} }
remoteName = await this.showAddRemotePrompt(
remoteUrl,
(await repo.getRemotes()).map(r => r.name),
);
let remoteName: string | undefined;
let secondaryRemoteName: string | undefined;
if (!remoteName) {
action = DeepLinkServiceAction.DeepLinkCancelled;
break;
if (remoteUrl && !remote) {
remoteName = await this.showAddRemotePrompt(
remoteUrl,
(await repo.getRemotes()).map(r => r.name),
);
if (remoteName) {
try {
await repo.addRemote(remoteName, remoteUrl, { fetch: true });
} catch {
action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Failed to add remote.';
break;
}
[this._context.remote] = await repo.getRemotes({ filter: r => r.url === remoteUrl });
if (!this._context.remote) {
action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Failed to add remote.';
break;
}
}
} }
try {
await repo.addRemote(remoteName, remoteUrl, { fetch: true });
} catch {
action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Failed to add remote.';
break;
if (secondaryRemoteUrl && !secondaryRemote) {
secondaryRemoteName = await this.showAddRemotePrompt(
secondaryRemoteUrl,
(await repo.getRemotes()).map(r => r.name),
);
if (secondaryRemoteName) {
try {
await repo.addRemote(secondaryRemoteName, secondaryRemoteUrl, { fetch: true });
} catch {
action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Failed to add remote.';
break;
}
[this._context.secondaryRemote] = await repo.getRemotes({
filter: r => r.url === secondaryRemoteUrl,
});
if (!this._context.secondaryRemote) {
action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Failed to add remote.';
break;
}
}
} }
[this._context.remote] = await repo.getRemotes({ filter: r => r.url === remoteUrl });
if (!this._context.remote) {
if (this._context.secondaryRemote && !this._context.remote) {
this._context.remote = this._context.secondaryRemote;
}
if (!remoteName && !secondaryRemoteName) {
action = DeepLinkServiceAction.DeepLinkCancelled;
} else if (!this._context.remote) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Failed to add remote.'; message = 'Failed to add remote.';
break;
} }
action = DeepLinkServiceAction.RemoteAdded; action = DeepLinkServiceAction.RemoteAdded;
break; break;
}
case DeepLinkServiceState.TargetMatch: case DeepLinkServiceState.TargetMatch:
case DeepLinkServiceState.FetchedTargetMatch:
case DeepLinkServiceState.FetchedTargetMatch: {
if (!repo || !targetType) { if (!repo || !targetType) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing repository or target type.'; message = 'Missing repository or target type.';
@ -694,8 +784,8 @@ export class DeepLinkService implements Disposable {
? DeepLinkServiceAction.TargetsMatched ? DeepLinkServiceAction.TargetsMatched
: DeepLinkServiceAction.TargetMatched; : DeepLinkServiceAction.TargetMatched;
break; break;
case DeepLinkServiceState.Fetch:
}
case DeepLinkServiceState.Fetch: {
if (!repo || !remote) { if (!repo || !remote) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing repository or remote.'; message = 'Missing repository or remote.';
@ -715,10 +805,16 @@ export class DeepLinkService implements Disposable {
break; break;
} }
if (secondaryRemote && secondaryRemote.name !== remote.name) {
try {
await repo.fetch({ remote: secondaryRemote.name, progress: true });
} catch {}
}
action = DeepLinkServiceAction.TargetFetched; action = DeepLinkServiceAction.TargetFetched;
break; break;
case DeepLinkServiceState.OpenGraph:
}
case DeepLinkServiceState.OpenGraph: {
if (!repo || !targetType) { if (!repo || !targetType) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing repository or target type.'; message = 'Missing repository or target type.';
@ -743,15 +839,20 @@ export class DeepLinkService implements Disposable {
action = DeepLinkServiceAction.DeepLinkResolved; action = DeepLinkServiceAction.DeepLinkResolved;
break; break;
case DeepLinkServiceState.OpenComparison:
}
case DeepLinkServiceState.OpenComparison: {
if (!repo) { if (!repo) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing repository.'; message = 'Missing repository.';
break; break;
} }
if (targetSha == null || secondaryTargetSha == null) {
if (
targetId == null ||
secondaryTargetId == null ||
targetSha == null ||
secondaryTargetSha == null
) {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Missing target or secondary target.'; message = 'Missing target or secondary target.';
break; break;
@ -759,18 +860,19 @@ export class DeepLinkService implements Disposable {
await this.container.searchAndCompareView.compare( await this.container.searchAndCompareView.compare(
repo.path, repo.path,
secondaryTargetSha === '' || isSha(secondaryTargetSha)
? secondaryTargetSha
: { label: secondaryTargetSha, ref: secondaryTargetSha },
targetSha === '' || isSha(targetSha) ? targetSha : { label: targetSha, ref: targetSha },
secondaryTargetId === '' || isSha(secondaryTargetId)
? secondaryTargetId
: { label: secondaryTargetId, ref: secondaryTargetSha },
targetId === '' || isSha(targetId) ? targetId : { label: targetId, ref: targetSha },
); );
action = DeepLinkServiceAction.DeepLinkResolved; action = DeepLinkServiceAction.DeepLinkResolved;
break; break;
default:
}
default: {
action = DeepLinkServiceAction.DeepLinkErrored; action = DeepLinkServiceAction.DeepLinkErrored;
message = 'Unknown state.'; message = 'Unknown state.';
break; break;
}
} }
} }
} }

Loading…
Cancel
Save