Skip to content

Commit

Permalink
GH-165: Updated Git API.
Browse files Browse the repository at this point in the history
Signed-off-by: Akos Kitta <kittaakos@gmail.com>
  • Loading branch information
kittaakos committed Sep 7, 2017
1 parent c490a7a commit 4302b5d
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 123 deletions.
183 changes: 163 additions & 20 deletions packages/git/src/common/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import { Account, Repository, RepositoryWithRemote, WorkingDirectoryStatus } from './model';
import { Repository, WorkingDirectoryStatus } from './model';

/**
* The WS endpoint path to the Git.
Expand All @@ -17,17 +17,154 @@ export const GitPath = '/services/git';
*/
export const Git = Symbol('Git');

export namespace Git {

/**
* Options for various Git commands.
*/
export namespace Options {

/**
* Git clone options.
*/
export interface Clone {

/**
* The desired destination path (given as a URI) for the cloned repository.
* If the path does not exist it will be created. Cloning into an existing
* directory is only allowed if the directory is empty. If not specified,
* the the workspace root will be used as the destination.
*/
readonly localUri?: string;

/**
* The branch to checkout after the clone has completed. If not given,
* the default branch will will be the current one which is usually the `master`.
*/
readonly branch?: string;

}

/**
* Options for further `git checkout` refinements.
*/
export namespace Checkout {

/**
* Options for checking out branches.
*/
export interface Branch {

/**
* Branch to checkout; if it refers to a branch, then that branch is checked out.
* Otherwise, if it refers to a valid commit, your `HEAD` becomes "detached" and you are no
* longer on any branch.
*/
readonly branch: string;

/**
* When switching branches, proceed even if the index or the working tree differs from `HEAD`.
* This is used to throw away local changes.
*/
readonly force?: boolean;

/**
* When switching branches, if you have local modifications to one or more files that are different
* between the current branch and the branch to which you are switching, the command refuses to
* switch branches in order to preserve your modifications in context. However, with this option,
* a three-way merge between the current branch, your working tree contents, and the new branch is done,
* and you will be on the new branch.
*/
readonly merge?: boolean;


/**
* The name for the new local branch.
*/
readonly newBranch?: string;

}

/**
* Options for checking out files from the working tree.
*/
export interface WorkingTreeFile {

/**
* This is used to restore modified or deleted paths to their original contents from the index or replace
* paths with the contents from a named tree-ish (most often a commit-ish).
*
* One can specify a regular expression for the paths, in such cases, it must be escaped with single-quotes.
* For instance checking out a `Hello.ts` file will be simply `"Hello.ts"`, if one would like to checkout
* all TS files, then this for should be used: ```ts
* const options = {
* paths: `'*.ts'`
* }
* ```.
*/
readonly paths: string | string[];

/**
* When checking out paths from the index, do not fail upon unmerged entries; instead, unmerged
* entries are ignored.
*/
readonly force?: boolean;

/**
* When checking out paths from the index, this option lets you recreate the conflicted merge in the
* specified paths.
*/
readonly merge?: boolean;

/**
* Tree to checkout from. If not specified, the index will be used.
*/
readonly treeish?: string;

}

}

/**
* Options for further refining the `git show` command.
*/
export interface Show {

/**
* The desired encoding which should be used when retrieving the file content.
* `utf8` should be used for text content and `binary` for blobs. The default one is `utf8`.
*/
readonly encoding?: 'utf8' | 'binary';

/**
* A commit SHA or some other identifier that ultimately dereferences to a commit/tree.
* `HEAD` is the local `HEAD`, and `index` is the the staged. If not specified,
* then `HEAD` will be used instead. But this can be `HEAD~2` or `ed14ef1~1` where `ed14ef1` is a commit hash.
*/
readonly commitish?: 'index' | string

}

}

}

/**
* Provides basic functionality for Git.
*
* TODOs:
* - static factory method for cloning. clone(localUri: string, remoteUrl: string): Promise<Repository>
* - register/remove repositories that are currently outside of the workspace but user wants to track the changes.
* - replace with HEAD (aka discard) should be supported by the `checkout` method.
* - get file content from HEAD as a string that can be reused by the diff-editor later. (first iteration)
*/
export interface Git {

/**
* Clones a remote repository into the desired local location.
*
* @param remoteUrl the URL of the remote.
* @param options the clone options.
*/
clone(remoteUrl: string, options?: Git.Options.Clone): Promise<Repository>;

/**
* Resolves to an array of repositories discovered in the workspace.
*/
Expand Down Expand Up @@ -96,13 +233,12 @@ export interface Git {
deleteBranch(repository: Repository, name: string): Promise<void>;

/**
* Switches to a branch in the repository.
* Switches branches or restores working tree files.
*
* @param repository the repository to which the branch has to be switched to.
* @param localName if specified, the remote branch will be pulled in with this, desired, name. Ignored when the branch already exists locally.
* @param name the name of the repository to switch to.
* @param repository the repository to where the `git checkout` has to be performed.
* @param options further checkout options.
*/
checkout(repository: Repository, name: string, localName?: string): Promise<void>;
checkout(repository: Repository, options: Git.Options.Checkout.Branch | Git.Options.Checkout.WorkingTreeFile): Promise<void>;

/**
* Commits the changes of all staged files in the working directory.
Expand All @@ -117,33 +253,30 @@ export interface Git {
* The remotely-tracked branches will be updated too.
*
* @param repository the repository to fetch from.
* @param account the account when authentication is required by the `remote` when fetching.
*/
fetch(repository: RepositoryWithRemote, account?: Account): Promise<void>;
fetch(repository: Repository): Promise<void>;

/**
* Updates the remote `refs` using local `refs`, while sending objects necessary to complete the given `refs` by pushing
* Updates the `refs` using local `refs`, while sending objects necessary to complete the given `refs` by pushing
* all committed changed from the local Git repository to the `remote` one.
*
* @param repository the remote repository to push to.
* @param account the account that is used for the authentication on the `remote` when performing the `git pull`.
* @param repository the repository to push to.
*/
push(repository: RepositoryWithRemote, account: Account): Promise<void>;
push(repository: Repository): Promise<void>;

/**
* Fetches from and integrates with another remote repository. It incorporates changes from a remote repository into the current branch.
* Fetches from and integrates with another repository. It incorporates changes from a repository into the current branch.
* In its default mode, `git pull` is shorthand for `git fetch` followed by `git merge FETCH_HEAD`.
*
* @param repository the remote repository to pull from.
* @param account the account when authenticating the user on the `remote`.
* @param repository the repository to pull from.
*/
pull(repository: RepositoryWithRemote, account?: Account): Promise<void>;
pull(repository: Repository): Promise<void>;

/**
* Resets the current `HEAD` of the entire working directory to the specified state.
*
* @param repository the repository which state has to be reset.
* @param mode the reset mode. The followings are supported: `hard`, `sort`, or `mixed`. Those correspond to the consecutive `--hard`, `--soft`, and `--mixed`.
* @param mode the reset mode. The followings are supported: `hard`, `sort`, or `mixed`. Those correspond to the consecutive `--hard`, `--soft`, and `--mixed` Git options.
* @param ref the reference to reset to. By default, resets to `HEAD`.
*/
reset(repository: Repository, mode: 'hard' | 'soft' | 'mixed', ref?: string): Promise<void>;
Expand All @@ -164,4 +297,14 @@ export interface Git {
*/
rebase(repository: Repository, name: string): Promise<void>;

/**
* Retrieves and shows the content of a resource from the repository at a given reference, commit, or tree.
* Resolves to a promise that will produce a Buffer instance containing the contents of the file or an error if the file does not exists in the given revision.
*
* @param repository the repository to get the file content from.
* @param uri the URI of the file who's content has to be retrieved and shown.
* @param options the options for further refining the `git show`.
*/
show(repository: Repository, uri: string, options?: Git.Options.Show): Promise<Buffer>

}
24 changes: 2 additions & 22 deletions packages/git/src/common/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,6 @@ export interface Repository {

}

/**
* Repository with the `remote` URL information.
*/
export interface RepositoryWithRemote extends Repository {

/**
* The remote URL of the local clone.
*/
readonly remoteUrl?: string;

}

export namespace Repository {

/**
Expand All @@ -166,21 +154,13 @@ export namespace Repository {
}

/**
* `true` if the argument is a type of a [RepositoryWithRemote](#RepositoryWithRemote), otherwise `false`.
*/
export function isRemote(repository: any | undefined): repository is RepositoryWithRemote {
return repository && typeof (<RepositoryWithRemote>repository).remoteUrl === 'string';
}

/**
* `true` if the arguments are equal. More precisely; when both the local and the remote repository URLs
* are equal.
* `true` if the arguments are equal. More precisely; when the local URIs are equal.
*
* @param left the repository to compare with the other.
* @param right the other repository.
*/
export function equals(left: Repository, right: Repository): boolean {
return left.localUri === right.localUri && (isRemote(left) ? left.remoteUrl : undefined) === (isRemote(right) ? right.remoteUrl : undefined);
return left.localUri === right.localUri;
}

/**
Expand Down
17 changes: 12 additions & 5 deletions packages/git/src/node/dugite-git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { getStatus } from 'dugite-extra/lib/command/status';
import { stage, unstage } from 'dugite-extra/lib/command/stage';
import { locateRepositories } from './git-repository-locator';
import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol';
import { Repository, RepositoryWithRemote, WorkingDirectoryStatus, FileChange, FileStatus } from '../common/model';
import { Repository, WorkingDirectoryStatus, FileChange, FileStatus } from '../common/model';
import { IStatusResult, IAheadBehind, AppFileStatus, WorkingDirectoryStatus as DugiteStatus, FileChange as DugiteFileChange } from 'dugite-extra/lib/model/status';

/**
Expand All @@ -27,6 +27,9 @@ export class DugiteGit implements Git {
@inject(WorkspaceServer) private readonly workspace: WorkspaceServer) {
}

async clone(remoteUrl: string, options?: Git.Options.Clone | undefined): Promise<Repository> {
throw new Error("Method not implemented.");
}
async repositories(): Promise<Repository[]> {
const workspaceRoot = await this.workspace.getRoot();
const path = await getFsPath(workspaceRoot);
Expand Down Expand Up @@ -72,23 +75,23 @@ export class DugiteGit implements Git {
throw new Error("Method not implemented.");
}

async checkout(repository: Repository, name: string, localName?: string): Promise<void> {
checkout(repository: Repository, options: Git.Options.Checkout.Branch | Git.Options.Checkout.WorkingTreeFile): Promise<void> {
throw new Error("Method not implemented.");
}

async commit(repository: Repository, message?: string): Promise<void> {
throw new Error("Method not implemented.");
}

async fetch(repository: RepositoryWithRemote): Promise<void> {
async fetch(repository: Repository): Promise<void> {
throw new Error("Method not implemented.");
}

async push(repository: RepositoryWithRemote): Promise<void> {
async push(repository: Repository): Promise<void> {
throw new Error("Method not implemented.");
}

async pull(repository: RepositoryWithRemote): Promise<void> {
async pull(repository: Repository): Promise<void> {
throw new Error("Method not implemented.");
}

Expand All @@ -104,6 +107,10 @@ export class DugiteGit implements Git {
throw new Error("Method not implemented.");
}

async show(repository: Repository, uri: string, options?: Git.Options.Show | undefined): Promise<Buffer> {
throw new Error("Method not implemented.");
}

private async getContainerRepository(path: string): Promise<Repository | undefined> {
try {
const result = await git(['rev-parse', '--show-toplevel'], path, 'rev-parse');
Expand Down
Loading

0 comments on commit 4302b5d

Please sign in to comment.