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

cleanup git relative path calculation

上级 3f9e7172
......@@ -337,6 +337,7 @@ export const GitErrorCodes = {
LocalChangesOverwritten: 'LocalChangesOverwritten',
NoUpstreamBranch: 'NoUpstreamBranch',
IsInSubmodule: 'IsInSubmodule',
WrongCase: 'WrongCase',
};
function getGitErrorCode(stderr: string): string | undefined {
......@@ -642,6 +643,36 @@ export function parseGitCommit(raw: string): Commit | null {
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 {
cached?: boolean;
}
......@@ -710,13 +741,19 @@ export class Repository {
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) {
return Promise.reject<Buffer>(new GitError({
const err = new GitError({
message: 'Could not show object.',
exitCode
}));
});
if (/exists on disk, but not in/.test(stderr)) {
err.gitErrorCode = GitErrorCodes.WrongCase;
}
return Promise.reject<Buffer>(err);
}
return stdout;
......@@ -751,25 +788,27 @@ export class Repository {
return { mode, object, size: parseInt(size) };
}
async lstreeOutput(treeish: string, path: string): Promise<string> {
if (!treeish) { // index
const { stdout } = await this.run(['ls-files', '--stage', '--', path]);
return stdout;
}
async lstree2(treeish: string, path: string): Promise<LsTreeElement[]> {
const { stdout } = await this.run(['ls-tree', treeish, '--', path]);
return parseLsTree(stdout);
}
const { stdout } = await this.run(['ls-tree', '-l', treeish, '--', path]);
return stdout;
async lsfiles(path: string): Promise<LsFilesElement[]> {
const { stdout } = await this.run(['ls-files', '--stage', '--', path]);
return parseLsFiles(stdout);
}
async relativePathToGitRelativePath(treeish: string, path: string): Promise<string> {
let gitPath: string = path;
const pathPrefix = path.substring(0, path.lastIndexOf('/'));
const lstOutput = await this.lstreeOutput(treeish, pathPrefix + '/');
const findResult = lstOutput.toUpperCase().indexOf(path.toUpperCase());
if (findResult) {
gitPath = lstOutput.substr(findResult, path.length);
async getGitRelativePath(treeish: string, relativePath: string): Promise<string> {
const relativePathLowercase = relativePath.toLowerCase();
const dirname = path.posix.dirname(relativePath) + '/';
const elements: { file: string; }[] = treeish ? await this.lstree2(treeish, dirname) : await this.lsfiles(dirname);
const element = elements.filter(file => file.file.toLowerCase() === relativePathLowercase)[0];
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 }> {
......
......@@ -876,8 +876,8 @@ export class Repository implements Disposable {
}
async show(ref: string, filePath: string): Promise<string> {
return this.run(Operation.Show, async () => {
let relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
return await this.run(Operation.Show, async () => {
const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
const configFiles = workspace.getConfiguration('files', Uri.file(filePath));
const defaultEncoding = configFiles.get<string>('encoding');
const autoGuessEncoding = configFiles.get<boolean>('autoGuessEncoding');
......@@ -886,8 +886,16 @@ export class Repository implements Disposable {
ref = 'HEAD';
}
relativePath = await this.repository.relativePathToGitRelativePath(ref, relativePath);
return this.repository.bufferString(`${ref}:${relativePath}`, defaultEncoding, autoGuessEncoding);
try {
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 @@
'use strict';
import 'mocha';
import { GitStatusParser, parseGitCommit, parseGitmodules } from '../git';
import { GitStatusParser, parseGitCommit, parseGitmodules, parseLsTree, parseLsFiles } from '../git';
import * as assert from 'assert';
suite('git', () => {
......@@ -177,7 +177,7 @@ suite('git', () => {
});
suite('parseGitCommit', () => {
test('single parent commit', () => {
test('single parent commit', function () {
const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1
8e5a374372b8393906c7e380dbb09349c5385554
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
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
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
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.
先完成此消息的编辑!
想要评论请 注册