import type { TextDocumentShowOptions } from 'vscode';
|
|
import { env, Range, Uri, window } from 'vscode';
|
|
import type {
|
|
BrowseRepoAtRevisionCommandArgs,
|
|
DiffWithCommandArgs,
|
|
DiffWithWorkingCommandArgs,
|
|
GitCommandsCommandArgs,
|
|
OpenFileOnRemoteCommandArgs,
|
|
OpenWorkingFileCommandArgs,
|
|
ShowQuickCommitCommandArgs,
|
|
ShowQuickCommitFileCommandArgs,
|
|
} from '../commands';
|
|
import type { FileAnnotationType } from '../configuration';
|
|
import { Commands, CoreCommands } from '../constants';
|
|
import { Container } from '../container';
|
|
import { GitUri } from '../git/gitUri';
|
|
import type { GitCommit, GitStashCommit } from '../git/models/commit';
|
|
import { isCommit } from '../git/models/commit';
|
|
import type { GitContributor } from '../git/models/contributor';
|
|
import type { GitFile } from '../git/models/file';
|
|
import type {
|
|
GitBranchReference,
|
|
GitReference,
|
|
GitRevisionReference,
|
|
GitStashReference,
|
|
GitTagReference,
|
|
} from '../git/models/reference';
|
|
import { GitRevision } from '../git/models/reference';
|
|
import type { GitRemote } from '../git/models/remote';
|
|
import type { Repository } from '../git/models/repository';
|
|
import type { GitWorktree } from '../git/models/worktree';
|
|
import { RepositoryPicker } from '../quickpicks/repositoryPicker';
|
|
import { ensure } from '../system/array';
|
|
import { executeCommand, executeCoreCommand, executeEditorCommand } from '../system/command';
|
|
import type { OpenWorkspaceLocation } from '../system/utils';
|
|
import { findOrOpenEditor, findOrOpenEditors, openWorkspace } from '../system/utils';
|
|
import type { ViewsWithRepositoryFolders } from '../views/viewBase';
|
|
import type { ResetGitCommandArgs } from './git/reset';
|
|
|
|
export async function executeGitCommand(args: GitCommandsCommandArgs): Promise<void> {
|
|
void (await executeCommand<GitCommandsCommandArgs>(Commands.GitCommands, args));
|
|
}
|
|
|
|
function ensureRepo(repo: string | Repository): Repository {
|
|
const repository = typeof repo === 'string' ? Container.instance.git.getRepository(repo) : repo;
|
|
if (repository == null) throw new Error('Repository not found');
|
|
return repository;
|
|
}
|
|
|
|
export namespace GitActions {
|
|
export async function browseAtRevision(uri: Uri, options?: { before?: boolean; openInNewWindow?: boolean }) {
|
|
void (await executeEditorCommand<BrowseRepoAtRevisionCommandArgs>(Commands.BrowseRepoAtRevision, undefined, {
|
|
uri: uri,
|
|
before: options?.before,
|
|
openInNewWindow: options?.openInNewWindow,
|
|
}));
|
|
}
|
|
|
|
export function cherryPick(repo?: string | Repository, refs?: GitRevisionReference | GitRevisionReference[]) {
|
|
return executeGitCommand({
|
|
command: 'cherry-pick',
|
|
state: { repo: repo, references: refs },
|
|
});
|
|
}
|
|
|
|
export function fetch(repos?: string | string[] | Repository | Repository[], ref?: GitBranchReference) {
|
|
return executeGitCommand({ command: 'fetch', state: { repos: repos, reference: ref } });
|
|
}
|
|
|
|
export function merge(repo?: string | Repository, ref?: GitReference) {
|
|
return executeGitCommand({ command: 'merge', state: { repo: repo, reference: ref } });
|
|
}
|
|
|
|
export function pull(repos?: string | string[] | Repository | Repository[], ref?: GitBranchReference) {
|
|
return executeGitCommand({ command: 'pull', state: { repos: repos, reference: ref } });
|
|
}
|
|
|
|
export function push(repos?: string | string[] | Repository | Repository[], force?: boolean, ref?: GitReference) {
|
|
return executeGitCommand({
|
|
command: 'push',
|
|
state: { repos: repos, flags: force ? ['--force'] : [], reference: ref },
|
|
});
|
|
}
|
|
|
|
export function rebase(repo?: string | Repository, ref?: GitReference, interactive: boolean = true) {
|
|
return executeGitCommand({
|
|
command: 'rebase',
|
|
state: { repo: repo, reference: ref, flags: interactive ? ['--interactive'] : [] },
|
|
});
|
|
}
|
|
|
|
export function reset(
|
|
repo?: string | Repository,
|
|
ref?: GitRevisionReference,
|
|
flags?: NonNullable<ResetGitCommandArgs['state']>['flags'],
|
|
) {
|
|
return executeGitCommand({
|
|
command: 'reset',
|
|
confirm: flags == null || flags.includes('--hard'),
|
|
state: { repo: repo, reference: ref, flags: flags },
|
|
});
|
|
}
|
|
|
|
export function revert(repo?: string | Repository, refs?: GitRevisionReference | GitRevisionReference[]) {
|
|
return executeGitCommand({
|
|
command: 'revert',
|
|
state: { repo: repo, references: refs },
|
|
});
|
|
}
|
|
|
|
export function switchTo(repos?: string | string[] | Repository | Repository[], ref?: GitReference) {
|
|
return executeGitCommand({
|
|
command: 'switch',
|
|
state: { repos: repos, reference: ref },
|
|
});
|
|
}
|
|
|
|
export namespace Branch {
|
|
export function create(repo?: string | Repository, ref?: GitReference, name?: string) {
|
|
return executeGitCommand({
|
|
command: 'branch',
|
|
state: {
|
|
subcommand: 'create',
|
|
repo: repo,
|
|
reference: ref,
|
|
name: name,
|
|
},
|
|
});
|
|
}
|
|
|
|
export function remove(repo?: string | Repository, refs?: GitBranchReference | GitBranchReference[]) {
|
|
return executeGitCommand({
|
|
command: 'branch',
|
|
state: {
|
|
subcommand: 'delete',
|
|
repo: repo,
|
|
references: refs,
|
|
},
|
|
});
|
|
}
|
|
|
|
export function rename(repo?: string | Repository, ref?: GitBranchReference, name?: string) {
|
|
return executeGitCommand({
|
|
command: 'branch',
|
|
state: {
|
|
subcommand: 'rename',
|
|
repo: repo,
|
|
reference: ref,
|
|
name: name,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function reveal(
|
|
branch: GitBranchReference,
|
|
options?: {
|
|
select?: boolean;
|
|
focus?: boolean;
|
|
expand?: boolean | number;
|
|
},
|
|
) {
|
|
const view = branch.remote ? Container.instance.remotesView : Container.instance.branchesView;
|
|
const node = view.canReveal
|
|
? await view.revealBranch(branch, options)
|
|
: await Container.instance.repositoriesView.revealBranch(branch, options);
|
|
return node;
|
|
}
|
|
}
|
|
|
|
export namespace Commit {
|
|
export async function applyChanges(
|
|
file: string | GitFile,
|
|
ref1: GitRevisionReference,
|
|
ref2?: GitRevisionReference,
|
|
) {
|
|
// Open the working file to ensure undo will work
|
|
await GitActions.Commit.openFile(file, ref1, { preserveFocus: true, preview: false });
|
|
|
|
let ref = ref1.ref;
|
|
// If the file is `?` (untracked), then this must be a stash, so get the ^3 commit to access the untracked file
|
|
if (typeof file !== 'string' && file.status === '?') {
|
|
ref = `${ref}^3`;
|
|
}
|
|
|
|
await Container.instance.git.applyChangesToWorkingFile(
|
|
GitUri.fromFile(file, ref1.repoPath, ref),
|
|
ref,
|
|
ref2?.ref,
|
|
);
|
|
}
|
|
|
|
export async function copyIdToClipboard(ref: { repoPath: string; ref: string } | GitCommit) {
|
|
await env.clipboard.writeText(ref.ref);
|
|
}
|
|
|
|
export async function copyMessageToClipboard(
|
|
ref: { repoPath: string; ref: string } | GitCommit,
|
|
): Promise<void> {
|
|
let commit;
|
|
if (isCommit(ref)) {
|
|
commit = ref;
|
|
if (commit.message == null) {
|
|
await commit.ensureFullDetails();
|
|
}
|
|
} else {
|
|
commit = await Container.instance.git.getCommit(ref.repoPath, ref.ref);
|
|
if (commit == null) return;
|
|
}
|
|
|
|
const message = commit.message ?? commit.summary;
|
|
await env.clipboard.writeText(message);
|
|
}
|
|
|
|
export async function openAllChanges(commit: GitCommit, options?: TextDocumentShowOptions): Promise<void>;
|
|
export async function openAllChanges(
|
|
files: GitFile[],
|
|
refs: { repoPath: string; ref1: string; ref2: string },
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openAllChanges(
|
|
commitOrFiles: GitCommit | GitFile[],
|
|
refsOrOptions: { repoPath: string; ref1: string; ref2: string } | TextDocumentShowOptions | undefined,
|
|
options?: TextDocumentShowOptions,
|
|
) {
|
|
let files;
|
|
let refs;
|
|
if (isCommit(commitOrFiles)) {
|
|
if (commitOrFiles.files == null) {
|
|
await commitOrFiles.ensureFullDetails();
|
|
}
|
|
|
|
files = commitOrFiles.files ?? [];
|
|
refs = {
|
|
repoPath: commitOrFiles.repoPath,
|
|
// Don't need to worry about verifying the previous sha, as the DiffWith command will
|
|
ref1: commitOrFiles.unresolvedPreviousSha,
|
|
ref2: commitOrFiles.sha,
|
|
};
|
|
|
|
options = refsOrOptions as TextDocumentShowOptions | undefined;
|
|
} else {
|
|
files = commitOrFiles;
|
|
refs = refsOrOptions as { repoPath: string; ref1: string; ref2: string };
|
|
}
|
|
|
|
if (files.length > 10) {
|
|
const result = await window.showWarningMessage(
|
|
`Are you sure you want to open the changes for all ${files.length} files?`,
|
|
{ title: 'Yes' },
|
|
{ title: 'No', isCloseAffordance: true },
|
|
);
|
|
if (result == null || result.title === 'No') return;
|
|
}
|
|
|
|
options = { preserveFocus: true, preview: false, ...options };
|
|
|
|
for (const file of files) {
|
|
await openChanges(file, refs, options);
|
|
}
|
|
}
|
|
|
|
export async function openAllChangesWithDiffTool(commit: GitCommit): Promise<void>;
|
|
export async function openAllChangesWithDiffTool(
|
|
files: GitFile[],
|
|
ref: { repoPath: string; ref: string },
|
|
): Promise<void>;
|
|
export async function openAllChangesWithDiffTool(
|
|
commitOrFiles: GitCommit | GitFile[],
|
|
ref?: { repoPath: string; ref: string },
|
|
) {
|
|
let files;
|
|
if (isCommit(commitOrFiles)) {
|
|
if (commitOrFiles.files == null) {
|
|
await commitOrFiles.ensureFullDetails();
|
|
}
|
|
|
|
files = commitOrFiles.files ?? [];
|
|
ref = {
|
|
repoPath: commitOrFiles.repoPath,
|
|
ref: commitOrFiles.sha,
|
|
};
|
|
} else {
|
|
files = commitOrFiles;
|
|
}
|
|
|
|
if (files.length > 10) {
|
|
const result = await window.showWarningMessage(
|
|
`Are you sure you want to open the changes for all ${files.length} files?`,
|
|
{ title: 'Yes' },
|
|
{ title: 'No', isCloseAffordance: true },
|
|
);
|
|
if (result == null || result.title === 'No') return;
|
|
}
|
|
|
|
for (const file of files) {
|
|
void openChangesWithDiffTool(file, ref!);
|
|
}
|
|
}
|
|
|
|
export async function openAllChangesWithWorking(
|
|
commit: GitCommit,
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openAllChangesWithWorking(
|
|
files: GitFile[],
|
|
ref: { repoPath: string; ref: string },
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openAllChangesWithWorking(
|
|
commitOrFiles: GitCommit | GitFile[],
|
|
refOrOptions: { repoPath: string; ref: string } | TextDocumentShowOptions | undefined,
|
|
options?: TextDocumentShowOptions,
|
|
) {
|
|
let files;
|
|
let ref;
|
|
if (isCommit(commitOrFiles)) {
|
|
if (commitOrFiles.files == null) {
|
|
await commitOrFiles.ensureFullDetails();
|
|
}
|
|
|
|
files = commitOrFiles.files ?? [];
|
|
ref = {
|
|
repoPath: commitOrFiles.repoPath,
|
|
ref: commitOrFiles.sha,
|
|
};
|
|
|
|
options = refOrOptions as TextDocumentShowOptions | undefined;
|
|
} else {
|
|
files = commitOrFiles;
|
|
ref = refOrOptions as { repoPath: string; ref: string };
|
|
}
|
|
|
|
if (files.length > 10) {
|
|
const result = await window.showWarningMessage(
|
|
`Are you sure you want to open the changes for all ${files.length} files?`,
|
|
{ title: 'Yes' },
|
|
{ title: 'No', isCloseAffordance: true },
|
|
);
|
|
if (result == null || result.title === 'No') return;
|
|
}
|
|
|
|
options = { preserveFocus: true, preview: false, ...options };
|
|
|
|
for (const file of files) {
|
|
await openChangesWithWorking(file, ref, options);
|
|
}
|
|
}
|
|
|
|
export async function openChanges(
|
|
file: string | GitFile,
|
|
commit: GitCommit,
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openChanges(
|
|
file: GitFile,
|
|
refs: { repoPath: string; ref1: string; ref2: string },
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openChanges(
|
|
file: string | GitFile,
|
|
commitOrRefs: GitCommit | { repoPath: string; ref1: string; ref2: string },
|
|
options?: TextDocumentShowOptions,
|
|
) {
|
|
if (typeof file === 'string') {
|
|
if (!isCommit(commitOrRefs)) throw new Error('Invalid arguments');
|
|
|
|
const f = await commitOrRefs.findFile(file);
|
|
if (f == null) throw new Error('Invalid arguments');
|
|
|
|
file = f;
|
|
}
|
|
|
|
if (file.status === 'A') return;
|
|
|
|
const refs = isCommit(commitOrRefs)
|
|
? {
|
|
repoPath: commitOrRefs.repoPath,
|
|
// Don't need to worry about verifying the previous sha, as the DiffWith command will
|
|
ref1: commitOrRefs.unresolvedPreviousSha,
|
|
ref2: commitOrRefs.sha,
|
|
}
|
|
: commitOrRefs;
|
|
|
|
options = { preserveFocus: true, preview: false, ...options };
|
|
|
|
const uri1 = GitUri.fromFile(file, refs.repoPath);
|
|
const uri2 =
|
|
file.status === 'R' || file.status === 'C'
|
|
? GitUri.fromFile(file, refs.repoPath, refs.ref2, true)
|
|
: uri1;
|
|
|
|
void (await executeCommand<DiffWithCommandArgs>(Commands.DiffWith, {
|
|
repoPath: refs.repoPath,
|
|
lhs: { uri: uri1, sha: refs.ref1 },
|
|
rhs: { uri: uri2, sha: refs.ref2 },
|
|
showOptions: options,
|
|
}));
|
|
}
|
|
|
|
export function openChangesWithDiffTool(
|
|
file: string | GitFile,
|
|
commit: GitCommit,
|
|
tool?: string,
|
|
): Promise<void>;
|
|
export function openChangesWithDiffTool(
|
|
file: GitFile,
|
|
ref: { repoPath: string; ref: string },
|
|
tool?: string,
|
|
): Promise<void>;
|
|
export async function openChangesWithDiffTool(
|
|
file: string | GitFile,
|
|
commitOrRef: GitCommit | { repoPath: string; ref: string },
|
|
tool?: string,
|
|
) {
|
|
if (typeof file === 'string') {
|
|
if (!isCommit(commitOrRef)) throw new Error('Invalid arguments');
|
|
|
|
const f = await commitOrRef.findFile(file);
|
|
if (f == null) throw new Error('Invalid arguments');
|
|
|
|
file = f;
|
|
}
|
|
|
|
return Container.instance.git.openDiffTool(
|
|
commitOrRef.repoPath,
|
|
GitUri.fromFile(file, file.repoPath ?? commitOrRef.repoPath),
|
|
{
|
|
ref1: GitRevision.isUncommitted(commitOrRef.ref) ? '' : `${commitOrRef.ref}^`,
|
|
ref2: GitRevision.isUncommitted(commitOrRef.ref) ? '' : commitOrRef.ref,
|
|
staged: GitRevision.isUncommittedStaged(commitOrRef.ref) || file.indexStatus != null,
|
|
tool: tool,
|
|
},
|
|
);
|
|
}
|
|
|
|
export async function openChangesWithWorking(
|
|
file: string | GitFile,
|
|
commit: GitCommit,
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openChangesWithWorking(
|
|
file: GitFile,
|
|
ref: { repoPath: string; ref: string },
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openChangesWithWorking(
|
|
file: string | GitFile,
|
|
commitOrRef: GitCommit | { repoPath: string; ref: string },
|
|
options?: TextDocumentShowOptions,
|
|
) {
|
|
if (typeof file === 'string') {
|
|
if (!isCommit(commitOrRef)) throw new Error('Invalid arguments');
|
|
|
|
const f = await commitOrRef.findFile(file);
|
|
if (f == null) throw new Error('Invalid arguments');
|
|
|
|
file = f;
|
|
}
|
|
|
|
if (file.status === 'D') return;
|
|
|
|
let ref;
|
|
if (isCommit(commitOrRef)) {
|
|
ref = {
|
|
repoPath: commitOrRef.repoPath,
|
|
ref: commitOrRef.sha,
|
|
};
|
|
} else {
|
|
ref = commitOrRef;
|
|
}
|
|
|
|
options = { preserveFocus: true, preview: false, ...options };
|
|
|
|
void (await executeEditorCommand<DiffWithWorkingCommandArgs>(Commands.DiffWithWorking, undefined, {
|
|
uri: GitUri.fromFile(file, ref.repoPath, ref.ref),
|
|
showOptions: options,
|
|
}));
|
|
}
|
|
|
|
export async function openDirectoryCompare(
|
|
repoPath: string,
|
|
ref: string,
|
|
ref2: string | undefined,
|
|
tool?: string,
|
|
): Promise<void> {
|
|
return Container.instance.git.openDirectoryCompare(repoPath, ref, ref2, tool);
|
|
}
|
|
|
|
export async function openDirectoryCompareWithPrevious(
|
|
ref: { repoPath: string; ref: string } | GitCommit,
|
|
): Promise<void> {
|
|
return openDirectoryCompare(ref.repoPath, ref.ref, `${ref.ref}^`);
|
|
}
|
|
|
|
export async function openDirectoryCompareWithWorking(
|
|
ref: { repoPath: string; ref: string } | GitCommit,
|
|
): Promise<void> {
|
|
return openDirectoryCompare(ref.repoPath, ref.ref, undefined);
|
|
}
|
|
|
|
export async function openFile(uri: Uri, options?: TextDocumentShowOptions): Promise<void>;
|
|
export async function openFile(
|
|
file: string | GitFile,
|
|
ref: GitRevisionReference,
|
|
options?: TextDocumentShowOptions,
|
|
): Promise<void>;
|
|
export async function openFile(
|
|
fileOrUri: string | GitFile | Uri,
|
|
refOrOptions?: GitRevisionReference | TextDocumentShowOptions,
|
|
options?: TextDocumentShowOptions,
|
|
) {
|
|
let uri;
|
|
if (fileOrUri instanceof Uri) {
|
|
uri = fileOrUri;
|
|
options = refOrOptions as TextDocumentShowOptions;
|
|
} else {
|
|
const ref = refOrOptions as GitRevisionReference;
|
|
|
|
uri = GitUri.fromFile(fileOrUri, ref.repoPath, ref.ref);
|
|
// If the file is `?` (untracked), then this must be an untracked file in a stash, so just return
|
|
if (typeof fileOrUri !== 'string' && fileOrUri.status === '?') return;
|
|
}
|
|
|
|
options = { preserveFocus: true, preview: false, ...options };
|
|
|
|
void (await executeEditorCommand<OpenWorkingFileCommandArgs>(Commands.OpenWorkingFile, undefined, {
|
|
uri: uri,
|
|
showOptions: options,
|
|
}));
|
|
}
|
|
|
|
export async function openFileAtRevision(
|
|
revisionUri: Uri,
|
|
options?: TextDocumentShowOptions & { annotationType?: FileAnnotationType; line?: number },
|
|
): Promise<void>;
|
|
export async function openFileAtRevision(
|
|
file: string | GitFile,
|
|
commit: GitCommit,
|
|
options?: TextDocumentShowOptions & { annotationType?: FileAnnotationType; line?: number },
|
|
): Promise<void>;
|
|
export async function openFileAtRevision(
|
|
fileOrRevisionUri: string | GitFile | Uri,
|
|
commitOrOptions?: GitCommit | TextDocumentShowOptions,
|
|
options?: TextDocumentShowOptions & { annotationType?: FileAnnotationType; line?: number },
|
|
): Promise<void> {
|
|
let uri;
|
|
if (fileOrRevisionUri instanceof Uri) {
|
|
if (isCommit(commitOrOptions)) throw new Error('Invalid arguments');
|
|
|
|
uri = fileOrRevisionUri;
|
|
options = commitOrOptions;
|
|
} else {
|
|
if (!isCommit(commitOrOptions)) throw new Error('Invalid arguments');
|
|
|
|
const commit = commitOrOptions;
|
|
|
|
let file;
|
|
if (typeof fileOrRevisionUri === 'string') {
|
|
const f = await commit.findFile(fileOrRevisionUri);
|
|
if (f == null) throw new Error('Invalid arguments');
|
|
|
|
file = f;
|
|
} else {
|
|
file = fileOrRevisionUri;
|
|
}
|
|
|
|
uri = Container.instance.git.getRevisionUri(
|
|
file.status === 'D' ? (await commit.getPreviousSha()) ?? GitRevision.deletedOrMissing : commit.sha,
|
|
file,
|
|
commit.repoPath,
|
|
);
|
|
}
|
|
|
|
const { annotationType, line, ...opts }: Exclude<typeof options, undefined> = {
|
|
preserveFocus: true,
|
|
preview: false,
|
|
...options,
|
|
};
|
|
|
|
if (line != null && line !== 0) {
|
|
opts.selection = new Range(line, 0, line, 0);
|
|
}
|
|
|
|
const editor = await findOrOpenEditor(uri, opts);
|
|
if (annotationType != null && editor != null) {
|
|
void (await Container.instance.fileAnnotations.show(editor, annotationType, {
|
|
selection: { line: line },
|
|
}));
|
|
}
|
|
}
|
|
|
|
export async function openFileOnRemote(uri: Uri): Promise<void>;
|
|
export async function openFileOnRemote(file: string | GitFile, ref: GitRevisionReference): Promise<void>;
|
|
export async function openFileOnRemote(
|
|
fileOrUri: string | GitFile | Uri,
|
|
ref?: GitRevisionReference,
|
|
): Promise<void> {
|
|
let uri;
|
|
if (fileOrUri instanceof Uri) {
|
|
uri = fileOrUri;
|
|
} else {
|
|
if (ref == null) throw new Error('Invalid arguments');
|
|
|
|
uri = GitUri.fromFile(fileOrUri, ref.repoPath, ref.ref);
|
|
// If the file is `?` (untracked), then this must be an untracked file in a stash, so just return
|
|
if (typeof fileOrUri !== 'string' && fileOrUri.status === '?') return;
|
|
}
|
|
|
|
void (await executeCommand<[Uri, OpenFileOnRemoteCommandArgs]>(Commands.OpenFileOnRemote, uri, {
|
|
sha: ref?.ref,
|
|
}));
|
|
}
|
|
|
|
export async function openFiles(commit: GitCommit): Promise<void>;
|
|
export async function openFiles(files: GitFile[], repoPath: string, ref: string): Promise<void>;
|
|
export async function openFiles(
|
|
commitOrFiles: GitCommit | GitFile[],
|
|
repoPath?: string,
|
|
ref?: string,
|
|
): Promise<void> {
|
|
let files;
|
|
if (isCommit(commitOrFiles)) {
|
|
if (commitOrFiles.files == null) {
|
|
await commitOrFiles.ensureFullDetails();
|
|
}
|
|
|
|
files = commitOrFiles.files ?? [];
|
|
repoPath = commitOrFiles.repoPath;
|
|
ref = commitOrFiles.sha;
|
|
} else {
|
|
files = commitOrFiles;
|
|
}
|
|
|
|
if (files.length > 10) {
|
|
const result = await window.showWarningMessage(
|
|
`Are you sure you want to open all ${files.length} files?`,
|
|
{ title: 'Yes' },
|
|
{ title: 'No', isCloseAffordance: true },
|
|
);
|
|
if (result == null || result.title === 'No') return;
|
|
}
|
|
|
|
const uris: Uri[] = (
|
|
await Promise.all(
|
|
files.map(file =>
|
|
Container.instance.git.getWorkingUri(repoPath!, GitUri.fromFile(file, repoPath!, ref)),
|
|
),
|
|
)
|
|
).filter(<T>(u?: T): u is T => Boolean(u));
|
|
findOrOpenEditors(uris);
|
|
}
|
|
|
|
export async function openFilesAtRevision(commit: GitCommit): Promise<void>;
|
|
export async function openFilesAtRevision(
|
|
files: GitFile[],
|
|
repoPath: string,
|
|
ref1: string,
|
|
ref2: string,
|
|
): Promise<void>;
|
|
export async function openFilesAtRevision(
|
|
commitOrFiles: GitCommit | GitFile[],
|
|
repoPath?: string,
|
|
ref1?: string,
|
|
ref2?: string,
|
|
): Promise<void> {
|
|
let files;
|
|
if (isCommit(commitOrFiles)) {
|
|
if (commitOrFiles.files == null) {
|
|
await commitOrFiles.ensureFullDetails();
|
|
}
|
|
|
|
files = commitOrFiles.files ?? [];
|
|
repoPath = commitOrFiles.repoPath;
|
|
ref1 = commitOrFiles.sha;
|
|
ref2 = await commitOrFiles.getPreviousSha();
|
|
} else {
|
|
files = commitOrFiles;
|
|
}
|
|
|
|
if (files.length > 10) {
|
|
const result = await window.showWarningMessage(
|
|
`Are you sure you want to open all ${files.length} file revisions?`,
|
|
{ title: 'Yes' },
|
|
{ title: 'No', isCloseAffordance: true },
|
|
);
|
|
if (result == null || result.title === 'No') return;
|
|
}
|
|
|
|
findOrOpenEditors(
|
|
files.map(file =>
|
|
Container.instance.git.getRevisionUri(file.status === 'D' ? ref2! : ref1!, file, repoPath!),
|
|
),
|
|
);
|
|
}
|
|
|
|
export async function restoreFile(file: string | GitFile, revision: GitRevisionReference) {
|
|
let path;
|
|
let ref;
|
|
if (typeof file === 'string') {
|
|
path = file;
|
|
ref = revision.ref;
|
|
} else {
|
|
path = file.path;
|
|
ref =
|
|
file.status === `?` ? `${revision.ref}^3` : file.status === 'D' ? `${revision.ref}^` : revision.ref;
|
|
}
|
|
|
|
await Container.instance.git.checkout(revision.repoPath, ref, { path: path });
|
|
}
|
|
|
|
export async function reveal(
|
|
commit: GitRevisionReference,
|
|
options?: {
|
|
select?: boolean;
|
|
focus?: boolean;
|
|
expand?: boolean | number;
|
|
},
|
|
) {
|
|
const views = [
|
|
Container.instance.commitsView,
|
|
Container.instance.branchesView,
|
|
Container.instance.remotesView,
|
|
];
|
|
|
|
// TODO@eamodio stop duplicate notifications
|
|
|
|
for (const view of views) {
|
|
const node = view.canReveal
|
|
? await view.revealCommit(commit, options)
|
|
: await Container.instance.repositoriesView.revealCommit(commit, options);
|
|
if (node != null) return node;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
export async function showDetailsQuickPick(commit: GitCommit, uri?: Uri): Promise<void>;
|
|
export async function showDetailsQuickPick(commit: GitCommit, file?: string | GitFile): Promise<void>;
|
|
export async function showDetailsQuickPick(
|
|
commit: GitCommit,
|
|
fileOrUri?: string | GitFile | Uri,
|
|
): Promise<void> {
|
|
if (fileOrUri == null) {
|
|
void (await executeCommand<ShowQuickCommitCommandArgs>(Commands.ShowQuickCommit, { commit: commit }));
|
|
return;
|
|
}
|
|
|
|
let uri;
|
|
if (fileOrUri instanceof Uri) {
|
|
uri = fileOrUri;
|
|
} else {
|
|
uri = GitUri.fromFile(fileOrUri, commit.repoPath, commit.ref);
|
|
}
|
|
|
|
void (await executeCommand<[Uri, ShowQuickCommitFileCommandArgs]>(Commands.ShowQuickCommitFile, uri, {
|
|
sha: commit.sha,
|
|
}));
|
|
}
|
|
|
|
export function showDetailsView(
|
|
commit: GitRevisionReference | GitCommit,
|
|
options?: { pin?: boolean; preserveFocus?: boolean },
|
|
): Promise<void> {
|
|
return Container.instance.commitDetailsView.show({ ...options, commit: commit });
|
|
}
|
|
}
|
|
|
|
export namespace Contributor {
|
|
export function addAuthors(repo?: string | Repository, contributors?: GitContributor | GitContributor[]) {
|
|
return executeGitCommand({
|
|
command: 'co-authors',
|
|
state: { repo: repo, contributors: contributors },
|
|
});
|
|
}
|
|
|
|
export async function reveal(
|
|
contributor: GitContributor,
|
|
options?: {
|
|
select?: boolean;
|
|
focus?: boolean;
|
|
expand?: boolean | number;
|
|
},
|
|
) {
|
|
const view = Container.instance.contributorsView;
|
|
const node = view.canReveal
|
|
? await view.revealContributor(contributor, options)
|
|
: await Container.instance.repositoriesView.revealContributor(contributor, options);
|
|
return node;
|
|
}
|
|
}
|
|
|
|
export namespace Remote {
|
|
export async function add(repo?: string | Repository) {
|
|
if (repo == null) {
|
|
repo = Container.instance.git.highlander;
|
|
|
|
if (repo == null) {
|
|
const pick = await RepositoryPicker.show(undefined, 'Choose a repository to add a remote to');
|
|
repo = pick?.item;
|
|
if (repo == null) return undefined;
|
|
}
|
|
}
|
|
|
|
const name = await window.showInputBox({
|
|
prompt: 'Please provide a name for the remote',
|
|
placeHolder: 'Remote name',
|
|
value: undefined,
|
|
ignoreFocusOut: true,
|
|
});
|
|
if (name == null || name.length === 0) return undefined;
|
|
|
|
const url = await window.showInputBox({
|
|
prompt: 'Please provide the repository url for the remote',
|
|
placeHolder: 'Remote repository url',
|
|
value: undefined,
|
|
ignoreFocusOut: true,
|
|
});
|
|
if (url == null || url.length === 0) return undefined;
|
|
|
|
repo = ensureRepo(repo);
|
|
await Container.instance.git.addRemote(repo.path, name, url);
|
|
await repo.fetch({ remote: name });
|
|
|
|
return name;
|
|
}
|
|
|
|
export async function fetch(repo: string | Repository, remote: string) {
|
|
if (typeof repo === 'string') {
|
|
const r = Container.instance.git.getRepository(repo);
|
|
if (r == null) return;
|
|
|
|
repo = r;
|
|
}
|
|
|
|
await repo.fetch({ remote: remote });
|
|
}
|
|
|
|
export async function prune(repo: string | Repository, remote: string) {
|
|
await Container.instance.git.pruneRemote(typeof repo === 'string' ? repo : repo.path, remote);
|
|
}
|
|
|
|
export async function reveal(
|
|
remote: GitRemote,
|
|
options?: {
|
|
select?: boolean;
|
|
focus?: boolean;
|
|
expand?: boolean | number;
|
|
},
|
|
) {
|
|
const view = Container.instance.remotesView;
|
|
const node = view.canReveal
|
|
? await view.revealRemote(remote, options)
|
|
: await Container.instance.repositoriesView.revealRemote(remote, options);
|
|
return node;
|
|
}
|
|
}
|
|
|
|
export namespace Repository {
|
|
export async function reveal(
|
|
repoPath: string,
|
|
view?: ViewsWithRepositoryFolders,
|
|
options?: {
|
|
select?: boolean;
|
|
focus?: boolean;
|
|
expand?: boolean | number;
|
|
},
|
|
) {
|
|
const node = view?.canReveal
|
|
? await view.revealRepository(repoPath, options)
|
|
: await Container.instance.repositoriesView.revealRepository(repoPath, options);
|
|
return node;
|
|
}
|
|
}
|
|
|
|
export namespace Stash {
|
|
export function apply(repo?: string | Repository, ref?: GitStashReference) {
|
|
return executeGitCommand({
|
|
command: 'stash',
|
|
state: { subcommand: 'apply', repo: repo, reference: ref },
|
|
});
|
|
}
|
|
|
|
export function drop(repo?: string | Repository, ref?: GitStashReference) {
|
|
return executeGitCommand({
|
|
command: 'stash',
|
|
state: { subcommand: 'drop', repo: repo, reference: ref },
|
|
});
|
|
}
|
|
|
|
export function pop(repo?: string | Repository, ref?: GitStashReference) {
|
|
return executeGitCommand({
|
|
command: 'stash',
|
|
state: { subcommand: 'pop', repo: repo, reference: ref },
|
|
});
|
|
}
|
|
|
|
export function push(repo?: string | Repository, uris?: Uri[], message?: string, keepStaged: boolean = false) {
|
|
return executeGitCommand({
|
|
command: 'stash',
|
|
state: {
|
|
subcommand: 'push',
|
|
repo: repo,
|
|
uris: uris,
|
|
message: message,
|
|
flags: keepStaged ? ['--keep-index'] : undefined,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function reveal(
|
|
stash: GitStashReference,
|
|
options?: {
|
|
select?: boolean;
|
|
focus?: boolean;
|
|
expand?: boolean | number;
|
|
},
|
|
) {
|
|
const view = Container.instance.stashesView;
|
|
const node = view.canReveal
|
|
? await view.revealStash(stash, options)
|
|
: await Container.instance.repositoriesView.revealStash(stash, options);
|
|
return node;
|
|
}
|
|
|
|
export function showDetailsView(
|
|
stash: GitStashReference | GitStashCommit,
|
|
options?: { pin?: boolean; preserveFocus?: boolean },
|
|
): Promise<void> {
|
|
return Container.instance.commitDetailsView.show({ ...options, commit: stash });
|
|
}
|
|
}
|
|
|
|
export namespace Tag {
|
|
export function create(repo?: string | Repository, ref?: GitReference, name?: string) {
|
|
return executeGitCommand({
|
|
command: 'tag',
|
|
state: {
|
|
subcommand: 'create',
|
|
repo: repo,
|
|
reference: ref,
|
|
name: name,
|
|
},
|
|
});
|
|
}
|
|
|
|
export function remove(repo?: string | Repository, refs?: GitTagReference | GitTagReference[]) {
|
|
return executeGitCommand({
|
|
command: 'tag',
|
|
state: {
|
|
subcommand: 'delete',
|
|
repo: repo,
|
|
references: refs,
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function reveal(
|
|
tag: GitTagReference,
|
|
options?: {
|
|
select?: boolean;
|
|
focus?: boolean;
|
|
expand?: boolean | number;
|
|
},
|
|
) {
|
|
const view = Container.instance.tagsView;
|
|
const node = view.canReveal
|
|
? await view.revealTag(tag, options)
|
|
: await Container.instance.repositoriesView.revealTag(tag, options);
|
|
return node;
|
|
}
|
|
}
|
|
|
|
export namespace Worktree {
|
|
export function create(repo?: string | Repository, uri?: Uri, ref?: GitReference) {
|
|
return executeGitCommand({
|
|
command: 'worktree',
|
|
state: { subcommand: 'create', repo: repo, uri: uri, reference: ref },
|
|
});
|
|
}
|
|
|
|
export function open(worktree: GitWorktree, options?: { location?: OpenWorkspaceLocation }) {
|
|
return openWorkspace(worktree.uri, options);
|
|
}
|
|
|
|
export function remove(repo?: string | Repository, uri?: Uri) {
|
|
return executeGitCommand({
|
|
command: 'worktree',
|
|
state: { subcommand: 'delete', repo: repo, uris: ensure(uri) },
|
|
});
|
|
}
|
|
|
|
export async function reveal(
|
|
worktree: GitWorktree,
|
|
options?: { select?: boolean; focus?: boolean; expand?: boolean | number },
|
|
) {
|
|
const view = Container.instance.worktreesView;
|
|
const node = view.canReveal
|
|
? await view.revealWorktree(worktree, options)
|
|
: await Container.instance.repositoriesView.revealWorktree(worktree, options);
|
|
return node;
|
|
}
|
|
|
|
export async function revealInFileExplorer(worktree: GitWorktree) {
|
|
void (await executeCoreCommand(CoreCommands.RevealInFileExplorer, worktree.uri));
|
|
}
|
|
}
|
|
}
|