Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/infoParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as xml2js from "xml2js";

export interface ISvnInfo {
kind: string;
path: string;
revision: string;
url: string;
relativeUrl: string;
repository: {
root: string;
uuid: string;
};
wcInfo: {
wcrootAbspath: string;
uuid: string;
};
commit: {
revision: string;
author: string;
date: string;
};
}

function camelcase(name: string) {
return name
.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {
return index == 0 ? letter.toLowerCase() : letter.toUpperCase();
})
.replace(/[\s\-]+/g, "");
}

export async function parseInfoXml(content: string): Promise<ISvnInfo> {
return new Promise<ISvnInfo>((resolve, reject) => {
xml2js.parseString(
content,
{
mergeAttrs: true,
explicitRoot: false,
explicitArray: false,
attrNameProcessors: [camelcase],
tagNameProcessors: [camelcase]
},
(err, result) => {
if (err || typeof result.entry === "undefined") {
reject();
}

resolve(result.entry);
}
);
});
}
13 changes: 7 additions & 6 deletions src/svn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as iconv from "iconv-lite";
import * as jschardet from "jschardet";
import * as path from "path";
import { Repository } from "./svnRepository";
import { parseInfoXml } from "./infoParser";

// List: https://github.com/apache/subversion/blob/1.6.x/subversion/svn/schema/status.rnc#L33
export enum Status {
Expand All @@ -21,7 +22,7 @@ export enum Status {
NORMAL = "normal",
OBSTRUCTED = "obstructed",
REPLACED = "replaced",
UNVERSIONED = "unversioned",
UNVERSIONED = "unversioned"
}

export interface CpOptions {
Expand Down Expand Up @@ -180,12 +181,12 @@ export class Svn {

async getRepositoryRoot(path: string) {
try {
let result = await this.exec(path, ["info", "--xml"]);
let rootPath = result.stdout.match(
/<wcroot-abspath>(.*)<\/wcroot-abspath>/i
)[1];
return rootPath;
const result = await this.exec(path, ["info", "--xml"]);

const info = await parseInfoXml(result.stdout);
return info.wcInfo.wcrootAbspath;
} catch (error) {
console.error(error);
throw new Error("Unable to find repository root path");
}
}
Expand Down
51 changes: 28 additions & 23 deletions src/svnRepository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { workspace } from "vscode";
import { Svn, CpOptions } from "./svn";
import { IFileStatus, parseStatusXml } from "./statusParser";
import { parseInfoXml, ISvnInfo } from "./infoParser";

export class Repository {
private _info?: ISvnInfo;

constructor(
private svn: Svn,
public root: string,
Expand All @@ -15,6 +18,22 @@ export class Repository {
return await parseStatusXml(result.stdout);
}

async getInfo(): Promise<ISvnInfo> {
if (this._info) {
return this._info;
}
const result = await this.svn.info(this.workspaceRoot);

this._info = await parseInfoXml(result.stdout);

//Cache for 30 seconds
setTimeout(() => {
this._info = undefined;
}, 30000);

return this._info;
}

async show(
path: string,
revision?: string,
Expand Down Expand Up @@ -46,11 +65,7 @@ export class Repository {
}

async getCurrentBranch(): Promise<string> {
const info = await this.svn.info(this.workspaceRoot);

if (info.exitCode !== 0) {
throw new Error(info.stderr);
}
const info = await this.getInfo();

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

const match = info.stdout.match(regex);
const match = info.url.match(regex);

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

const info = await this.svn.info(this.workspaceRoot);

if (info.exitCode !== 0) {
throw new Error(info.stderr);
}
const info = await this.getInfo();

let repoUrl = info.stdout.match(/<root>(.*?)<\/root>/)[1];
const match = info.stdout.match(regex);
let repoUrl = info.repository.root;
const match = info.url.match(regex);

if (match && match[1]) {
repoUrl = match[1];
Expand Down Expand Up @@ -188,14 +197,10 @@ export class Repository {

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

if (result.exitCode !== 0) {
throw new Error(result.stderr);
}

const switchBranch = await this.svn.switchBranch(
this.workspaceRoot,
newBranch
Expand Down