Skip to content

Commit

Permalink
GH-165: Fixed git show on Windows.
Browse files Browse the repository at this point in the history
  • Loading branch information
kittaakos committed Oct 6, 2017
1 parent 1af7bd8 commit e6dbb49
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/git/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"@theia/preferences-api": "^0.1.1",
"@theia/workspace": "^0.1.1",
"abs": "^1.3.8",
"dugite-extra": "0.0.1-alpha.11",
"dugite-extra": "0.0.1-alpha.12",
"findit2": "^2.2.3"
},
"publishConfig": {
Expand Down
19 changes: 9 additions & 10 deletions packages/git/src/browser/git-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ import { injectable, inject } from "inversify";
import { Git, Repository } from '../common';
import { Resource, ResourceResolver } from "@theia/core";
import URI from "@theia/core/lib/common/uri";
import { FileUri } from "@theia/core/lib/node/file-uri";

export const GIT_RESOURCE_SCHEME = 'gitrev';

export class GitResource implements Resource {

constructor(readonly uri: URI, protected readonly repository: Repository, protected readonly git: Git) { }

readContents(options?: { encoding?: string | undefined; } | undefined): Promise<string> {
return this.git.show(this.repository, this.uri.toString(), Object.assign({
commitish: this.uri.query
}, options)).then(content => content.toString());
async readContents(options?: { encoding?: string | undefined; } | undefined): Promise<string> {
return await this.git.show(this.repository, this.uri.toString(), Object.assign({ commitish: this.uri.query }, options));
}

dispose(): void { }
Expand All @@ -35,7 +32,7 @@ export class GitResourceResolver implements ResourceResolver {

resolve(uri: URI): Resource | Promise<Resource> {
if (uri.scheme !== GIT_RESOURCE_SCHEME) {
throw new Error("The given uri is not a git uri: " + uri);
throw new Error(`Expected a URI with ${GIT_RESOURCE_SCHEME} scheme. Was: ${uri}.`);
}
return this.getResource(uri);
}
Expand All @@ -46,18 +43,20 @@ export class GitResourceResolver implements ResourceResolver {
}

async getRepository(uri: URI): Promise<Repository> {
const uriWoS = uri.withoutScheme();
const dirStr = FileUri.fsPath(uriWoS);
const uriWithoutScheme = uri.withoutScheme();
const repos = await this.git.repositories();
// We sort by length so that we visit the nested repositories first.
// We do not want to get the repository A instead of B if we have:
// repository A, another repository B inside A and a resource A/B/C.ext.
const sortedRepos = repos.sort((a, b) => b.localUri.length - a.localUri.length);
for (const repo of sortedRepos) {
const localUri = new URI(repo.localUri);
// make sure that localUri of repo has no scheme.
const localUriStr = localUri.withoutScheme().toString();
if (dirStr.toString().startsWith(localUriStr)) {
if (uriWithoutScheme.toString().startsWith(localUriStr)) {
return { localUri: localUriStr };
}
}
return repos[0];
}
}
}
2 changes: 1 addition & 1 deletion packages/git/src/common/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ export interface Git {
* @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<String>;
show(repository: Repository, uri: string, options?: Git.Options.Show): Promise<string>;

}

Expand Down
50 changes: 49 additions & 1 deletion packages/git/src/node/dugite-git.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as fs from 'fs-extra';
import { expect } from 'chai';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { DugiteGit } from './dugite-git';
import { WorkingDirectoryStatus } from '../common/model';
import { WorkingDirectoryStatus, Repository } from '../common/model';
import { initRepository, createTestRepository } from 'dugite-extra/lib/command/test-helper';
import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol';

Expand Down Expand Up @@ -163,6 +163,54 @@ describe('git', async () => {

});

describe('show', async () => {

let repository: Repository | undefined;
let git: DugiteGit | undefined;

beforeEach(async () => {
const root = await createTestRepository(track.mkdirSync('status-test'));
const localUri = FileUri.create(root).toString();
repository = { localUri };
git = await createGit(root);
});

it('modified in working directory', async () => {
const repositoryPath = FileUri.fsPath(repository!.localUri);
fs.writeFileSync(path.join(repositoryPath, 'A.txt'), 'new content');
expect(fs.readFileSync(path.join(repositoryPath, 'A.txt'), { encoding: 'utf8' })).to.be.equal('new content');
const content = await git!.show(repository!, FileUri.create(path.join(repositoryPath, 'A.txt')).toString(), { commitish: 'HEAD' });
expect(content).to.be.equal('A');
});

it('modified in working directory (nested)', async () => {
const repositoryPath = FileUri.fsPath(repository!.localUri);
fs.writeFileSync(path.join(repositoryPath, 'folder', 'C.txt'), 'new content');
expect(fs.readFileSync(path.join(repositoryPath, 'folder', 'C.txt'), { encoding: 'utf8' })).to.be.equal('new content');
const content = await git!.show(repository!, FileUri.create(path.join(repositoryPath, 'folder', 'C.txt')).toString(), { commitish: 'HEAD' });
expect(content).to.be.equal('C');
});

it('modified in index', async () => {
const repositoryPath = FileUri.fsPath(repository!.localUri);
fs.writeFileSync(path.join(repositoryPath, 'A.txt'), 'new content');
expect(fs.readFileSync(path.join(repositoryPath, 'A.txt'), { encoding: 'utf8' })).to.be.equal('new content');
await git!.add(repository!, FileUri.create(path.join(repositoryPath, 'A.txt')).toString());
const content = await git!.show(repository!, FileUri.create(path.join(repositoryPath, 'A.txt')).toString(), { commitish: 'index' });
expect(content).to.be.equal('new content');
});

it('modified in index and in working directory', async () => {
const repositoryPath = FileUri.fsPath(repository!.localUri);
fs.writeFileSync(path.join(repositoryPath, 'A.txt'), 'new content');
expect(fs.readFileSync(path.join(repositoryPath, 'A.txt'), { encoding: 'utf8' })).to.be.equal('new content');
await git!.add(repository!, FileUri.create(path.join(repositoryPath, 'A.txt')).toString());
expect(await git!.show(repository!, FileUri.create(path.join(repositoryPath, 'A.txt')).toString(), { commitish: 'index' })).to.be.equal('new content');
expect(await git!.show(repository!, FileUri.create(path.join(repositoryPath, 'A.txt')).toString(), { commitish: 'HEAD' })).to.be.equal('A');
});

});

});

async function createGit(fsRoot: string = ''): Promise<DugiteGit> {
Expand Down
23 changes: 20 additions & 3 deletions packages/git/src/node/dugite-git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*/

import * as fs from 'fs';
import * as Path from 'path';
import { Git, GitUtils } from '../common/git';
import { git } from 'dugite-extra/lib/core/git';
import { injectable, inject } from "inversify";
import { push } from 'dugite-extra/lib/command/push';
import { pull } from 'dugite-extra/lib/command/pull';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { isWindows } from '@theia/core/lib/common/os';
import { clone } from 'dugite-extra/lib/command/clone';
import { fetch } from 'dugite-extra/lib/command/fetch';
import { merge } from 'dugite-extra/lib/command/merge';
import { FileUri } from '@theia/core/lib/node/file-uri';
import { getStatus } from 'dugite-extra/lib/command/status';
import { locateRepositories } from './git-repository-locator';
import { createCommit } from 'dugite-extra/lib/command/commit';
Expand All @@ -35,6 +37,7 @@ export class DugiteGit implements Git {

constructor(
@inject(WorkspaceServer) protected readonly workspace: WorkspaceServer) {
process.env.USE_LOCAL_GIT = 'true';
}

async clone(remoteUrl: string, options?: Git.Options.Clone): Promise<Repository> {
Expand All @@ -51,8 +54,22 @@ export class DugiteGit implements Git {
const repositories = await repositoriesPromise;
const containerRepository = await containerRepositoryPromise;
// Make sure not to add the container to the repositories twice. Can happen when WS root is a git repository.
if (containerRepository && repositories.map(r => r.localUri).indexOf(containerRepository.localUri) === -1) {
repositories.unshift(containerRepository);
if (containerRepository) {
// Below URIs point to the same location, but their `toString()` are not the same.
// file:///c%3A/Users/KITTAA~1/AppData/Local/Temp/discovery-test-211796-8240-vm7sah.wow0b/BASE',
// file:///c%3A/Users/kittaakos/AppData/Local/Temp/discovery-test-211796-8240-vm7sah.wow0b/BASE
// Is there a better way to compare NTFS/FAT 8.3 path formats on Windows?
let toCompareString = (path: string) => path;
if (isWindows) {
// We could add another optimization: if any of the path strings contain `~` then run the below logic, otherwise just return with the path.
toCompareString = (path: string) => JSON.stringify(fs.statSync(path));
}
const subRepositoryPaths = repositories.map(r => Path.resolve(this.getFsPath(r.localUri)));
const containerRepositoryPath = Path.resolve(this.getFsPath(containerRepository.localUri));

if (!subRepositoryPaths.some(p => toCompareString(p) === toCompareString(containerRepositoryPath))) {
repositories.unshift(containerRepository);
}
}
return repositories;
}
Expand Down
10 changes: 4 additions & 6 deletions packages/git/src/node/git-repository-locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,17 @@ const finder: any = require('findit2');
*/
export function locateRepositories(path: string): Promise<Repository[]> {
return new Promise<Repository[]>((resolve, reject) => {
const repositories: Repository[] = [];
const repositoryPaths = new Set();
const emitter = finder(abs(path));
emitter.on('directory', (dir: string, stat: fs.Stats, stop: () => void) => {
const base = Path.basename(dir);
if (base === '.git') {
const localUri = FileUri.create(Path.dirname(dir)).toString();
if (!localUri.endsWith(path)) {
repositories.push({ localUri });
}
const dirName = Path.dirname(dir);
repositoryPaths.add(dirName);
stop();
}
});
emitter.on('end', () => resolve(repositories));
emitter.on('end', () => resolve([...repositoryPaths].map(p => <Repository>{ localUri: FileUri.create(p).toString() })));
emitter.on('error', (error: Error) => reject(error));
});
}
22 changes: 19 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1922,13 +1922,14 @@ dtrace-provider@~0.8:
dependencies:
nan "^2.3.3"

dugite-extra@0.0.1-alpha.11:
version "0.0.1-alpha.11"
resolved "https://registry.yarnpkg.com/dugite-extra/-/dugite-extra-0.0.1-alpha.11.tgz#02200316acb7166e7f8021ef3af815e09c1dcaca"
dugite-extra@0.0.1-alpha.12:
version "0.0.1-alpha.12"
resolved "https://registry.yarnpkg.com/dugite-extra/-/dugite-extra-0.0.1-alpha.12.tgz#8b394ca35fc86afed746b3a1d88c3f927bb7ee3d"
dependencies:
byline "^5.0.0"
dugite "1.42.0"
find-git-exec "0.0.1-alpha.2"
upath "^1.0.0"

dugite@1.42.0:
version "1.42.0"
Expand Down Expand Up @@ -4039,6 +4040,10 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"

lodash@3.x:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"

lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@^4.8.0, lodash@~4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
Expand Down Expand Up @@ -6747,6 +6752,10 @@ unc-path-regex@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"

underscore.string@2.3.x:
version "2.3.3"
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.3.3.tgz#71c08bf6b428b1133f37e78fa3a21c82f7329b0d"

uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
Expand All @@ -6773,6 +6782,13 @@ unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"

upath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.0.tgz#b4706b9461ca8473adf89133d235689ca17f3656"
dependencies:
lodash "3.x"
underscore.string "2.3.x"

upper-case-first@^1.1.0, upper-case-first@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
Expand Down

0 comments on commit e6dbb49

Please sign in to comment.