Skip to content

Commit fc88008

Browse files
committed
fixes #119
1 parent e210a9e commit fc88008

File tree

8 files changed

+145
-11
lines changed

8 files changed

+145
-11
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# **v1.10.0**
2+
3+
## What's New
4+
5+
* @JohnstonCode Added conflict support
6+
17
# **v1.9.0**
28

39
## What's New

package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svn-scm",
33
"displayName": "SVN",
44
"description": "Integrated Subversion source control",
5-
"version": "1.9.0",
5+
"version": "1.10.0",
66
"publisher": "johnstoncode",
77
"engines": {
88
"vscode": "^1.17.0"
@@ -121,6 +121,11 @@
121121
"command": "svn.remove",
122122
"title": "Remove Selected",
123123
"category": "SVN"
124+
},
125+
{
126+
"command": "svn.resolve",
127+
"title": "Resolve Conflicts",
128+
"category": "SVN"
124129
}
125130
],
126131
"menus": {
@@ -142,6 +147,10 @@
142147
{
143148
"command": "svn.patch",
144149
"when": "config.svn.enabled"
150+
},
151+
{
152+
"command": "svn.resolve",
153+
"when": "config.svn.enabled"
145154
}
146155
],
147156
"scm/resourceGroup/context": [],

src/commands.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { Resource } from "./resource";
1616
import { toSvnUri } from "./uri";
1717
import * as path from "path";
1818
import { start } from "repl";
19+
import { getConflictPickOptions } from "./conflictItems";
1920

2021
interface CommandOptions {
2122
repository?: boolean;
@@ -177,7 +178,7 @@ export class SvnCommands {
177178
picks.push(new ChangeListItem(repository.changes));
178179
}
179180

180-
const svnConfig = workspace.getConfiguration("svn", Uri.file(repository.workspaceRoot));
181+
const svnConfig = workspace.getConfiguration("svn");
181182
const ignoreOnCommitList = svnConfig.get<string[]>(
182183
"sourceControl.ignoreOnCommit",
183184
[]
@@ -550,6 +551,30 @@ export class SvnCommands {
550551
}
551552
}
552553

554+
@command("svn.resolve", { repository: true })
555+
async resolve(repository: Repository) {
556+
const conflicts = repository.conflicts.resourceStates;
557+
558+
if (!conflicts.length) {
559+
window.showInformationMessage("No Conflicts");
560+
}
561+
562+
for (const conflict of conflicts) {
563+
const placeHolder = `Select conflict option for ${
564+
conflict.resourceUri.path
565+
}`;
566+
const picks = getConflictPickOptions();
567+
568+
const choice = await window.showQuickPick(picks, { placeHolder });
569+
570+
if (!choice) {
571+
return;
572+
}
573+
574+
await repository.resolve(conflict.resourceUri.path, choice.label);
575+
}
576+
}
577+
553578
private runByRepository<T>(
554579
resource: Uri,
555580
fn: (repository: Repository, resource: Uri) => Promise<T>

src/conflictItems.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { QuickPickItem } from "vscode";
2+
3+
interface ConflictOption {
4+
label: string;
5+
description: string;
6+
}
7+
8+
const ConflictOptions = [
9+
{
10+
label: "base",
11+
description:
12+
"Choose the file that was the (unmodified) BASE revision before you tried to integrate changes"
13+
},
14+
{
15+
label: "working",
16+
description:
17+
"Assuming that you've manually handled the conflict resolution, choose the version of the file as it currently stands in your working copy."
18+
},
19+
{
20+
label: "mine-full",
21+
description:
22+
"Preserve all local modifications and discarding all changes fetched"
23+
},
24+
{
25+
label: "theirs-full",
26+
description:
27+
"Discard all local modifications and integrating all changes fetched"
28+
},
29+
{
30+
label: "mine-conflict",
31+
description:
32+
"Resolve conflicted files by preferring local modifications over the changes fetched"
33+
},
34+
{
35+
label: "theirs-conflict",
36+
description:
37+
"Resolve conflicted files by preferring the changes fetched from the server over local modifications"
38+
}
39+
];
40+
41+
class ConflictItem implements QuickPickItem {
42+
constructor(private option: ConflictOption) {}
43+
44+
get label(): string {
45+
return this.option.label;
46+
}
47+
48+
get description(): string {
49+
return this.option.description;
50+
}
51+
}
52+
53+
export function getConflictPickOptions() {
54+
return ConflictOptions.map(option => new ConflictItem(option));
55+
}

src/repository.ts

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class Repository {
2929
public unversioned: SourceControlResourceGroup;
3030
public external: SourceControlResourceGroup;
3131
public changelists: Map<string, SourceControlResourceGroup> = new Map();
32+
public conflicts: SourceControlResourceGroup;
3233
private disposables: Disposable[] = [];
3334
public currentBranch = "";
3435
public isSwitchingBranch: boolean = false;
@@ -133,10 +134,15 @@ export class Repository {
133134
"external",
134135
"External"
135136
);
137+
this.conflicts = this.sourceControl.createResourceGroup(
138+
"conflicts",
139+
"conflicts"
140+
);
136141

137142
this.changes.hideWhenEmpty = true;
138143
this.unversioned.hideWhenEmpty = true;
139144
this.external.hideWhenEmpty = true;
145+
this.conflicts.hideWhenEmpty = true;
140146

141147
this.disposables.push(
142148
toDisposable(() => clearInterval(this.branchesTimer))
@@ -163,14 +169,15 @@ export class Repository {
163169
let changes: any[] = [];
164170
let unversioned: any[] = [];
165171
let external: any[] = [];
172+
let conflicts: any[] = [];
166173
let changelists: Map<string, Resource[]> = new Map();
167174

168175
const statuses = (await this.repository.getStatus()) || [];
169176

170-
const fileConfig = workspace.getConfiguration("files");
177+
const fileConfig = workspace.getConfiguration("files", Uri.file(this.root));
171178
const svnConfig = workspace.getConfiguration("svn");
172179

173-
const filesToExclude = fileConfig.get<any>("exclude", null);
180+
const filesToExclude = fileConfig.get<any>("exclude");
174181

175182
let excludeList: string[] = [];
176183
for (const pattern in filesToExclude) {
@@ -188,15 +195,20 @@ export class Repository {
188195
? Uri.file(path.join(this.workspaceRoot, status.rename))
189196
: undefined;
190197

191-
const resouce = new Resource(uri, status.status, renameUri, status.props);
198+
const resource = new Resource(
199+
uri,
200+
status.status,
201+
renameUri,
202+
status.props
203+
);
192204

193205
if (status.status === Status.NORMAL && status.props === PropStatus.NONE) {
194206
// On commit, `svn status` return all locked files with status="normal" and props="none"
195207
return;
196-
} else if (status.status === Status.UNVERSIONED) {
197-
unversioned.push(resouce);
198208
} else if (status.status === Status.EXTERNAL) {
199-
external.push(resouce);
209+
external.push(resource);
210+
} else if (status.status === Status.CONFLICTED) {
211+
conflicts.push(resource);
200212
} else {
201213
if (status.status === Status.UNVERSIONED) {
202214
const matches = status.path.match(
@@ -210,24 +222,27 @@ export class Repository {
210222
statuses.some(s => s.path === matches[1])
211223
) {
212224
return;
225+
} else {
226+
unversioned.push(resource);
213227
}
214228
}
215229

216230
if (!status.changelist) {
217-
changes.push(resouce);
231+
changes.push(resource);
218232
} else {
219233
let changelist = changelists.get(status.changelist);
220234
if (!changelist) {
221235
changelist = [];
222236
}
223-
changelist.push(resouce);
237+
changelist.push(resource);
224238
changelists.set(status.changelist, changelist);
225239
}
226240
}
227241
});
228242

229243
this.changes.resourceStates = changes;
230244
this.unversioned.resourceStates = unversioned;
245+
this.conflicts.resourceStates = conflicts;
231246

232247
this.changelists.forEach((group, changelist) => {
233248
group.resourceStates = [];
@@ -329,4 +344,13 @@ export class Repository {
329344
this._onDidChangeBranch.fire();
330345
}
331346
}
347+
348+
async resolve(file: string, action: string) {
349+
try {
350+
const response = await this.repository.resolve(file, action);
351+
window.showInformationMessage(response);
352+
} catch (error) {
353+
window.showErrorMessage(error);
354+
}
355+
}
332356
}

src/resource.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export class Resource implements SourceControlResourceState {
5353
return this._resourceUri;
5454
}
5555

56+
@memoize
5657
get type(): String {
5758
return this._type;
5859
}
@@ -86,7 +87,7 @@ export class Resource implements SourceControlResourceState {
8687
if (this.type === Status.ADDED && this.renameResourceUri) {
8788
return Resource.Icons[theme]["Renamed"];
8889
}
89-
90+
9091
const type = this.type.charAt(0).toUpperCase() + this.type.slice(1);
9192

9293
if (typeof Resource.Icons[theme][type] !== "undefined") {

src/svn.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,8 @@ export class Svn {
306306

307307
return this.exec("", args);
308308
}
309+
310+
resolve(file: string, action: string) {
311+
return this.exec("", ["resolve", "--accept", action, file]);
312+
}
309313
}

src/svnRepository.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,4 +290,14 @@ export class Repository {
290290

291291
return result.stdout;
292292
}
293+
294+
async resolve(file: string, action: string) {
295+
const result = await this.svn.resolve(file, action);
296+
297+
if (result.exitCode !== 0) {
298+
throw new Error(result.stderr);
299+
}
300+
301+
return result.stdout;
302+
}
293303
}

0 commit comments

Comments
 (0)