Skip to content

Commit 9ab9020

Browse files
authored
Merge pull request #115 from edgardmessias/xmlparser_info
Added xml parser for svn info
2 parents 88ed080 + 22e1f34 commit 9ab9020

File tree

3 files changed

+87
-29
lines changed

3 files changed

+87
-29
lines changed

src/infoParser.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import * as xml2js from "xml2js";
2+
3+
export interface ISvnInfo {
4+
kind: string;
5+
path: string;
6+
revision: string;
7+
url: string;
8+
relativeUrl: string;
9+
repository: {
10+
root: string;
11+
uuid: string;
12+
};
13+
wcInfo: {
14+
wcrootAbspath: string;
15+
uuid: string;
16+
};
17+
commit: {
18+
revision: string;
19+
author: string;
20+
date: string;
21+
};
22+
}
23+
24+
function camelcase(name: string) {
25+
return name
26+
.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {
27+
return index == 0 ? letter.toLowerCase() : letter.toUpperCase();
28+
})
29+
.replace(/[\s\-]+/g, "");
30+
}
31+
32+
export async function parseInfoXml(content: string): Promise<ISvnInfo> {
33+
return new Promise<ISvnInfo>((resolve, reject) => {
34+
xml2js.parseString(
35+
content,
36+
{
37+
mergeAttrs: true,
38+
explicitRoot: false,
39+
explicitArray: false,
40+
attrNameProcessors: [camelcase],
41+
tagNameProcessors: [camelcase]
42+
},
43+
(err, result) => {
44+
if (err || typeof result.entry === "undefined") {
45+
reject();
46+
}
47+
48+
resolve(result.entry);
49+
}
50+
);
51+
});
52+
}

src/svn.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as iconv from "iconv-lite";
55
import * as jschardet from "jschardet";
66
import * as path from "path";
77
import { Repository } from "./svnRepository";
8+
import { parseInfoXml } from "./infoParser";
89

910
// List: https://github.com/apache/subversion/blob/1.6.x/subversion/svn/schema/status.rnc#L33
1011
export enum Status {
@@ -21,7 +22,7 @@ export enum Status {
2122
NORMAL = "normal",
2223
OBSTRUCTED = "obstructed",
2324
REPLACED = "replaced",
24-
UNVERSIONED = "unversioned",
25+
UNVERSIONED = "unversioned"
2526
}
2627

2728
export interface CpOptions {
@@ -180,12 +181,12 @@ export class Svn {
180181

181182
async getRepositoryRoot(path: string) {
182183
try {
183-
let result = await this.exec(path, ["info", "--xml"]);
184-
let rootPath = result.stdout.match(
185-
/<wcroot-abspath>(.*)<\/wcroot-abspath>/i
186-
)[1];
187-
return rootPath;
184+
const result = await this.exec(path, ["info", "--xml"]);
185+
186+
const info = await parseInfoXml(result.stdout);
187+
return info.wcInfo.wcrootAbspath;
188188
} catch (error) {
189+
console.error(error);
189190
throw new Error("Unable to find repository root path");
190191
}
191192
}

src/svnRepository.ts

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { workspace } from "vscode";
22
import { Svn, CpOptions } from "./svn";
33
import { IFileStatus, parseStatusXml } from "./statusParser";
4+
import { parseInfoXml, ISvnInfo } from "./infoParser";
45

56
export class Repository {
7+
private _info?: ISvnInfo;
8+
69
constructor(
710
private svn: Svn,
811
public root: string,
@@ -15,6 +18,22 @@ export class Repository {
1518
return await parseStatusXml(result.stdout);
1619
}
1720

21+
async getInfo(): Promise<ISvnInfo> {
22+
if (this._info) {
23+
return this._info;
24+
}
25+
const result = await this.svn.info(this.workspaceRoot);
26+
27+
this._info = await parseInfoXml(result.stdout);
28+
29+
//Cache for 30 seconds
30+
setTimeout(() => {
31+
this._info = undefined;
32+
}, 30000);
33+
34+
return this._info;
35+
}
36+
1837
async show(
1938
path: string,
2039
revision?: string,
@@ -46,11 +65,7 @@ export class Repository {
4665
}
4766

4867
async getCurrentBranch(): Promise<string> {
49-
const info = await this.svn.info(this.workspaceRoot);
50-
51-
if (info.exitCode !== 0) {
52-
throw new Error(info.stderr);
53-
}
68+
const info = await this.getInfo();
5469

5570
const config = workspace.getConfiguration("svn");
5671
const trunkLayout = config.get<string>("layout.trunk");
@@ -60,11 +75,9 @@ export class Repository {
6075
const trees = [trunkLayout, branchesLayout, tagsLayout].filter(
6176
x => x != null
6277
);
63-
const regex = new RegExp(
64-
"<url>(.*?)/(" + trees.join("|") + ")(/([^/]+))?.*?</url>"
65-
);
78+
const regex = new RegExp("(.*?)/(" + trees.join("|") + ")(/([^/]+))?.*?");
6679

67-
const match = info.stdout.match(regex);
80+
const match = info.url.match(regex);
6881

6982
if (match) {
7083
if (match[4] && match[2] !== trunkLayout) {
@@ -87,16 +100,12 @@ export class Repository {
87100
const trees = [trunkLayout, branchesLayout, tagsLayout].filter(
88101
x => x != null
89102
);
90-
const regex = new RegExp("<url>(.*?)/(" + trees.join("|") + ").*?</url>");
103+
const regex = new RegExp("(.*?)/(" + trees.join("|") + ").*?");
91104

92-
const info = await this.svn.info(this.workspaceRoot);
93-
94-
if (info.exitCode !== 0) {
95-
throw new Error(info.stderr);
96-
}
105+
const info = await this.getInfo();
97106

98-
let repoUrl = info.stdout.match(/<root>(.*?)<\/root>/)[1];
99-
const match = info.stdout.match(regex);
107+
let repoUrl = info.repository.root;
108+
const match = info.url.match(regex);
100109

101110
if (match && match[1]) {
102111
repoUrl = match[1];
@@ -188,14 +197,10 @@ export class Repository {
188197

189198
const repoUrl = await this.getRepoUrl();
190199
const newBranch = repoUrl + "/" + branchesLayout + "/" + name;
191-
const resultBranch = await this.svn.info(this.workspaceRoot);
192-
const currentBranch = resultBranch.stdout.match(/<url>(.*?)<\/url>/)[1];
200+
const info = await this.getInfo();
201+
const currentBranch = info.url;
193202
const result = await this.svn.copy(currentBranch, newBranch, name);
194203

195-
if (result.exitCode !== 0) {
196-
throw new Error(result.stderr);
197-
}
198-
199204
const switchBranch = await this.svn.switchBranch(
200205
this.workspaceRoot,
201206
newBranch

0 commit comments

Comments
 (0)