提交 00a1abe9 编写于 作者: J Joao Moreno

cleanup git relative path calculation

上级 3f9e7172
...@@ -337,6 +337,7 @@ export const GitErrorCodes = { ...@@ -337,6 +337,7 @@ export const GitErrorCodes = {
LocalChangesOverwritten: 'LocalChangesOverwritten', LocalChangesOverwritten: 'LocalChangesOverwritten',
NoUpstreamBranch: 'NoUpstreamBranch', NoUpstreamBranch: 'NoUpstreamBranch',
IsInSubmodule: 'IsInSubmodule', IsInSubmodule: 'IsInSubmodule',
WrongCase: 'WrongCase',
}; };
function getGitErrorCode(stderr: string): string | undefined { function getGitErrorCode(stderr: string): string | undefined {
...@@ -642,6 +643,36 @@ export function parseGitCommit(raw: string): Commit | null { ...@@ -642,6 +643,36 @@ export function parseGitCommit(raw: string): Commit | null {
return { hash: match[1], message: match[3], parents }; return { hash: match[1], message: match[3], parents };
} }
interface LsTreeElement {
mode: string;
type: string;
object: string;
file: string;
}
export function parseLsTree(raw: string): LsTreeElement[] {
return raw.split('\n')
.filter(l => !!l)
.map(line => /^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/.exec(line)!)
.filter(m => !!m)
.map(([, mode, type, object, file]) => ({ mode, type, object, file }));
}
interface LsFilesElement {
mode: string;
object: string;
stage: string;
file: string;
}
export function parseLsFiles(raw: string): LsFilesElement[] {
return raw.split('\n')
.filter(l => !!l)
.map(line => /^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/.exec(line)!)
.filter(m => !!m)
.map(([, mode, object, stage, file]) => ({ mode, object, stage, file }));
}
export interface DiffOptions { export interface DiffOptions {
cached?: boolean; cached?: boolean;
} }
...@@ -710,13 +741,19 @@ export class Repository { ...@@ -710,13 +741,19 @@ export class Repository {
return Promise.reject<Buffer>('Can\'t open file from git'); return Promise.reject<Buffer>('Can\'t open file from git');
} }
const { exitCode, stdout } = await exec(child); const { exitCode, stdout, stderr } = await exec(child);
if (exitCode) { if (exitCode) {
return Promise.reject<Buffer>(new GitError({ const err = new GitError({
message: 'Could not show object.', message: 'Could not show object.',
exitCode exitCode
})); });
if (/exists on disk, but not in/.test(stderr)) {
err.gitErrorCode = GitErrorCodes.WrongCase;
}
return Promise.reject<Buffer>(err);
} }
return stdout; return stdout;
...@@ -751,25 +788,27 @@ export class Repository { ...@@ -751,25 +788,27 @@ export class Repository {
return { mode, object, size: parseInt(size) }; return { mode, object, size: parseInt(size) };
} }
async lstreeOutput(treeish: string, path: string): Promise<string> { async lstree2(treeish: string, path: string): Promise<LsTreeElement[]> {
if (!treeish) { // index const { stdout } = await this.run(['ls-tree', treeish, '--', path]);
const { stdout } = await this.run(['ls-files', '--stage', '--', path]); return parseLsTree(stdout);
return stdout; }
}
const { stdout } = await this.run(['ls-tree', '-l', treeish, '--', path]); async lsfiles(path: string): Promise<LsFilesElement[]> {
return stdout; const { stdout } = await this.run(['ls-files', '--stage', '--', path]);
return parseLsFiles(stdout);
} }
async relativePathToGitRelativePath(treeish: string, path: string): Promise<string> { async getGitRelativePath(treeish: string, relativePath: string): Promise<string> {
let gitPath: string = path; const relativePathLowercase = relativePath.toLowerCase();
const pathPrefix = path.substring(0, path.lastIndexOf('/')); const dirname = path.posix.dirname(relativePath) + '/';
const lstOutput = await this.lstreeOutput(treeish, pathPrefix + '/'); const elements: { file: string; }[] = treeish ? await this.lstree2(treeish, dirname) : await this.lsfiles(dirname);
const findResult = lstOutput.toUpperCase().indexOf(path.toUpperCase()); const element = elements.filter(file => file.file.toLowerCase() === relativePathLowercase)[0];
if (findResult) {
gitPath = lstOutput.substr(findResult, path.length); if (!element) {
throw new GitError({ message: 'Git relative path not found.' });
} }
return gitPath;
return element.file;
} }
async detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> { async detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> {
......
...@@ -876,8 +876,8 @@ export class Repository implements Disposable { ...@@ -876,8 +876,8 @@ export class Repository implements Disposable {
} }
async show(ref: string, filePath: string): Promise<string> { async show(ref: string, filePath: string): Promise<string> {
return this.run(Operation.Show, async () => { return await this.run(Operation.Show, async () => {
let relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/'); const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
const configFiles = workspace.getConfiguration('files', Uri.file(filePath)); const configFiles = workspace.getConfiguration('files', Uri.file(filePath));
const defaultEncoding = configFiles.get<string>('encoding'); const defaultEncoding = configFiles.get<string>('encoding');
const autoGuessEncoding = configFiles.get<boolean>('autoGuessEncoding'); const autoGuessEncoding = configFiles.get<boolean>('autoGuessEncoding');
...@@ -886,8 +886,16 @@ export class Repository implements Disposable { ...@@ -886,8 +886,16 @@ export class Repository implements Disposable {
ref = 'HEAD'; ref = 'HEAD';
} }
relativePath = await this.repository.relativePathToGitRelativePath(ref, relativePath); try {
return this.repository.bufferString(`${ref}:${relativePath}`, defaultEncoding, autoGuessEncoding); return await this.repository.bufferString(`${ref}:${relativePath}`, defaultEncoding, autoGuessEncoding);
} catch (err) {
if (err.gitErrorCode === GitErrorCodes.WrongCase) {
const gitRelativePath = await this.repository.getGitRelativePath(ref, relativePath);
return await this.repository.bufferString(`${ref}:${gitRelativePath}`, defaultEncoding, autoGuessEncoding);
}
throw err;
}
}); });
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
'use strict'; 'use strict';
import 'mocha'; import 'mocha';
import { GitStatusParser, parseGitCommit, parseGitmodules } from '../git'; import { GitStatusParser, parseGitCommit, parseGitmodules, parseLsTree, parseLsFiles } from '../git';
import * as assert from 'assert'; import * as assert from 'assert';
suite('git', () => { suite('git', () => {
...@@ -177,7 +177,7 @@ suite('git', () => { ...@@ -177,7 +177,7 @@ suite('git', () => {
}); });
suite('parseGitCommit', () => { suite('parseGitCommit', () => {
test('single parent commit', () => { test('single parent commit', function () {
const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1 const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1
8e5a374372b8393906c7e380dbb09349c5385554 8e5a374372b8393906c7e380dbb09349c5385554
This is a commit message.`; This is a commit message.`;
...@@ -189,7 +189,7 @@ This is a commit message.`; ...@@ -189,7 +189,7 @@ This is a commit message.`;
}); });
}); });
test('multiple parent commits', () => { test('multiple parent commits', function () {
const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217 8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
This is a commit message.`; This is a commit message.`;
...@@ -201,7 +201,7 @@ This is a commit message.`; ...@@ -201,7 +201,7 @@ This is a commit message.`;
}); });
}); });
test('no parent commits', async () => { test('no parent commits', function () {
const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1 const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
This is a commit message.`; This is a commit message.`;
...@@ -213,4 +213,68 @@ This is a commit message.`; ...@@ -213,4 +213,68 @@ This is a commit message.`;
}); });
}); });
}); });
suite('parseLsTree', function () {
test('sample', function () {
const input = `040000 tree 0274a81f8ee9ca3669295dc40f510bd2021d0043 .vscode
100644 blob 1d487c1817262e4f20efbfa1d04c18f51b0046f6 Screen Shot 2018-06-01 at 14.48.05.png
100644 blob 686c16e4f019b734655a2576ce8b98749a9ffdb9 Screen Shot 2018-06-07 at 20.04.59.png
100644 blob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 boom.txt
100644 blob 86dc360dd25f13fa50ffdc8259e9653921f4f2b7 boomcaboom.txt
100644 blob a68b14060589b16d7ac75f67b905c918c03c06eb file.js
100644 blob f7bcfb05af46850d780f88c069edcd57481d822d file.md
100644 blob ab8b86114a051f6490f1ec5e3141b9a632fb46b5 hello.js
100644 blob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 what.js
100644 blob be859e3f412fa86513cd8bebe8189d1ea1a3e46d what.txt
100644 blob 56ec42c9dc6fcf4534788f0fe34b36e09f37d085 what.txt2`;
const output = parseLsTree(input);
assert.deepEqual(output, [
{ mode: '040000', type: 'tree', object: '0274a81f8ee9ca3669295dc40f510bd2021d0043', file: '.vscode' },
{ mode: '100644', type: 'blob', object: '1d487c1817262e4f20efbfa1d04c18f51b0046f6', file: 'Screen Shot 2018-06-01 at 14.48.05.png' },
{ mode: '100644', type: 'blob', object: '686c16e4f019b734655a2576ce8b98749a9ffdb9', file: 'Screen Shot 2018-06-07 at 20.04.59.png' },
{ mode: '100644', type: 'blob', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', file: 'boom.txt' },
{ mode: '100644', type: 'blob', object: '86dc360dd25f13fa50ffdc8259e9653921f4f2b7', file: 'boomcaboom.txt' },
{ mode: '100644', type: 'blob', object: 'a68b14060589b16d7ac75f67b905c918c03c06eb', file: 'file.js' },
{ mode: '100644', type: 'blob', object: 'f7bcfb05af46850d780f88c069edcd57481d822d', file: 'file.md' },
{ mode: '100644', type: 'blob', object: 'ab8b86114a051f6490f1ec5e3141b9a632fb46b5', file: 'hello.js' },
{ mode: '100644', type: 'blob', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', file: 'what.js' },
{ mode: '100644', type: 'blob', object: 'be859e3f412fa86513cd8bebe8189d1ea1a3e46d', file: 'what.txt' },
{ mode: '100644', type: 'blob', object: '56ec42c9dc6fcf4534788f0fe34b36e09f37d085', file: 'what.txt2' }
]);
});
});
suite('parseLsFiles', function () {
test('sample', function () {
const input = `100644 7a73a41bfdf76d6f793007240d80983a52f15f97 0 .vscode/settings.json
100644 1d487c1817262e4f20efbfa1d04c18f51b0046f6 0 Screen Shot 2018-06-01 at 14.48.05.png
100644 686c16e4f019b734655a2576ce8b98749a9ffdb9 0 Screen Shot 2018-06-07 at 20.04.59.png
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0 boom.txt
100644 86dc360dd25f13fa50ffdc8259e9653921f4f2b7 0 boomcaboom.txt
100644 a68b14060589b16d7ac75f67b905c918c03c06eb 0 file.js
100644 f7bcfb05af46850d780f88c069edcd57481d822d 0 file.md
100644 ab8b86114a051f6490f1ec5e3141b9a632fb46b5 0 hello.js
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0 what.js
100644 be859e3f412fa86513cd8bebe8189d1ea1a3e46d 0 what.txt
100644 56ec42c9dc6fcf4534788f0fe34b36e09f37d085 0 what.txt2`;
const output = parseLsFiles(input);
assert.deepEqual(output, [
{ mode: '100644', object: '7a73a41bfdf76d6f793007240d80983a52f15f97', stage: '0', file: '.vscode/settings.json' },
{ mode: '100644', object: '1d487c1817262e4f20efbfa1d04c18f51b0046f6', stage: '0', file: 'Screen Shot 2018-06-01 at 14.48.05.png' },
{ mode: '100644', object: '686c16e4f019b734655a2576ce8b98749a9ffdb9', stage: '0', file: 'Screen Shot 2018-06-07 at 20.04.59.png' },
{ mode: '100644', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', stage: '0', file: 'boom.txt' },
{ mode: '100644', object: '86dc360dd25f13fa50ffdc8259e9653921f4f2b7', stage: '0', file: 'boomcaboom.txt' },
{ mode: '100644', object: 'a68b14060589b16d7ac75f67b905c918c03c06eb', stage: '0', file: 'file.js' },
{ mode: '100644', object: 'f7bcfb05af46850d780f88c069edcd57481d822d', stage: '0', file: 'file.md' },
{ mode: '100644', object: 'ab8b86114a051f6490f1ec5e3141b9a632fb46b5', stage: '0', file: 'hello.js' },
{ mode: '100644', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', stage: '0', file: 'what.js' },
{ mode: '100644', object: 'be859e3f412fa86513cd8bebe8189d1ea1a3e46d', stage: '0', file: 'what.txt' },
{ mode: '100644', object: '56ec42c9dc6fcf4534788f0fe34b36e09f37d085', stage: '0', file: 'what.txt2' },
]);
});
});
}); });
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册