Skip to content

Commit 020d0e8

Browse files
committed
graphql,web: Simple resolve conflicts from the web
1 parent 49f6e7b commit 020d0e8

File tree

15 files changed

+359
-15
lines changed

15 files changed

+359
-15
lines changed

graphql-server/schema.gql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ type Mutation {
416416
doltClone(databaseName: String!, ownerName: String!, remoteDbName: String!): Boolean!
417417
loadDataFile(schemaName: String, tableName: String!, refName: String!, databaseName: String!, importOp: ImportOperation!, fileType: FileType!, file: Upload!, modifier: LoadDataModifier): Boolean!
418418
mergePull(fromBranchName: String!, toBranchName: String!, databaseName: String!, author: AuthorInfo): Boolean!
419+
mergeAndResolveConflicts(author: AuthorInfo, fromBranchName: String!, toBranchName: String!, databaseName: String!, conflictResolveType: ConflictResolveType!): Boolean!
419420
addRemote(remoteName: String!, databaseName: String!, remoteUrl: String!): String!
420421
deleteRemote(databaseName: String!, remoteName: String!): Boolean!
421422
pullFromRemote(remoteName: String!, databaseName: String!, refName: String!, branchName: String!): PullRes!
@@ -448,4 +449,9 @@ enum LoadDataModifier {
448449
input AuthorInfo {
449450
name: String!
450451
email: String!
452+
}
453+
454+
enum ConflictResolveType {
455+
Ours
456+
Theirs
451457
}

graphql-server/src/pulls/pull.enums.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ export enum PullState {
77
}
88

99
registerEnumType(PullState, { name: "PullState" });
10+
11+
export enum ConflictResolveType {
12+
Ours = "ours",
13+
Theirs = "theirs",
14+
}
15+
16+
registerEnumType(ConflictResolveType, { name: "ConflictResolveType" });

graphql-server/src/pulls/pull.resolver.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { CommitResolver } from "../commits/commit.resolver";
1010
import { ConnectionProvider } from "../connections/connection.provider";
1111
import { AuthorInfo, PullArgs } from "../utils/commonTypes";
12+
import { ConflictResolveType } from "./pull.enums";
1213
import { PullWithDetails, fromAPIModelPullWithDetails } from "./pull.model";
1314

1415
@ArgsType()
@@ -17,6 +18,12 @@ class MergePullArgs extends PullArgs {
1718
author?: AuthorInfo;
1819
}
1920

21+
@ArgsType()
22+
class MergeAndResolveArgs extends MergePullArgs {
23+
@Field(_type => ConflictResolveType)
24+
conflictResolveType: ConflictResolveType;
25+
}
26+
2027
@Resolver(_of => PullWithDetails)
2128
export class PullResolver {
2229
constructor(
@@ -48,4 +55,19 @@ export class PullResolver {
4855
});
4956
return true;
5057
}
58+
59+
@Mutation(_returns => Boolean)
60+
async mergeAndResolveConflicts(
61+
@Args() args: MergeAndResolveArgs,
62+
): Promise<boolean> {
63+
const conn = this.conn.connection();
64+
await conn.callMergeWithResolveConflicts({
65+
databaseName: args.databaseName,
66+
fromBranchName: args.fromBranchName,
67+
toBranchName: args.toBranchName,
68+
author: args.author,
69+
conflictResolveType: args.conflictResolveType,
70+
});
71+
return true;
72+
}
5173
}

graphql-server/src/queryFactory/dolt/index.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,40 @@ export class DoltQueryFactory
326326
);
327327
}
328328

329+
async callMergeWithResolveConflicts(
330+
args: t.BranchesArgs & {
331+
author?: t.CommitAuthor;
332+
conflictResolveType: string;
333+
},
334+
): Promise<boolean> {
335+
return this.queryMultiple(
336+
async query => {
337+
await query("BEGIN");
338+
339+
const msg = `Merge branch ${args.fromBranchName}`;
340+
const params = [msg];
341+
if (args.author) {
342+
params.push(getAuthorString(args.author));
343+
}
344+
const res = await query(qh.getCallMerge(!!args.author), [
345+
args.fromBranchName,
346+
...params,
347+
]);
348+
349+
if (res.length && res[0].conflicts !== "0") {
350+
await query(qh.resolveConflicts, [`--${args.conflictResolveType}`]);
351+
await query(qh.getCommitMerge(!!args.author), params);
352+
} else {
353+
throw new Error("expected conflicts but none found");
354+
}
355+
356+
return true;
357+
},
358+
args.databaseName,
359+
args.toBranchName,
360+
);
361+
}
362+
329363
async resolveRefs(
330364
args: t.RefsArgs & { type?: CommitDiffType },
331365
): t.CommitsRes {

graphql-server/src/queryFactory/dolt/queries.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ export const mergeConflictsSummaryQuery = `SELECT * FROM DOLT_PREVIEW_MERGE_CONF
8787

8888
export const mergeConflictsQuery = `SELECT * FROM DOLT_PREVIEW_MERGE_CONFLICTS(?, ?, ?) LIMIT ? OFFSET ?`;
8989

90+
export const resolveConflicts = `CALL DOLT_CONFLICTS_RESOLVE(?, '.')`;
91+
92+
export const getCommitMerge = (hasAuthor = false) =>
93+
`CALL DOLT_COMMIT("-Am", ?${getAuthorNameString(hasAuthor)})`;
94+
9095
// REMOTES
9196

9297
export const callAddRemote = `CALL DOLT_REMOTE("add", ?, ?)`;

graphql-server/src/queryFactory/doltgres/index.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,40 @@ export class DoltgresQueryFactory
336336
);
337337
}
338338

339+
async callMergeWithResolveConflicts(
340+
args: t.BranchesArgs & {
341+
author?: t.CommitAuthor;
342+
conflictResolveType: string;
343+
},
344+
): Promise<boolean> {
345+
return this.queryMultiple(
346+
async query => {
347+
await query("BEGIN");
348+
349+
const msg = `Merge branch ${args.fromBranchName}`;
350+
const params = [msg];
351+
if (args.author) {
352+
params.push(getAuthorString(args.author));
353+
}
354+
const res = await query(qh.getCallMerge(!!args.author), [
355+
args.fromBranchName,
356+
...params,
357+
]);
358+
359+
if (res.length && res[0].conflicts !== "0") {
360+
await query(qh.resolveConflicts, [`--${args.conflictResolveType}`]);
361+
await query(qh.getCommitMerge(!!args.author), params);
362+
} else {
363+
throw new Error("expected conflicts but none found");
364+
}
365+
366+
return true;
367+
},
368+
args.databaseName,
369+
args.toBranchName,
370+
);
371+
}
372+
339373
async resolveRefs(
340374
args: t.RefsArgs & { type?: CommitDiffType },
341375
): t.CommitsRes {

graphql-server/src/queryFactory/doltgres/queries.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ export const mergeConflictsSummaryQuery = `SELECT * FROM DOLT_PREVIEW_MERGE_CONF
9595
export const getMergeConflictsQuery = (offset: number) =>
9696
`SELECT * FROM DOLT_PREVIEW_MERGE_CONFLICTS($1::text, $2::text, $3::text) LIMIT ${ROW_LIMIT + 1} OFFSET ${offset}`;
9797

98+
export const resolveConflicts = `SELECT DOLT_CONFLICTS_RESOLVE($1::text, '.')`;
99+
100+
export const getCommitMerge = (hasAuthor = false) =>
101+
`CALL DOLT_COMMIT("-Am", $1::text${getAuthorNameString(hasAuthor, "$2::text")})`;
102+
98103
// TAGS
99104

100105
export const callDeleteTag = `SELECT DOLT_TAG('-d', $1::text)`;

graphql-server/src/queryFactory/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ export declare class QueryFactory {
139139
args: t.BranchesArgs & { author?: t.CommitAuthor },
140140
): Promise<boolean>;
141141

142+
callMergeWithResolveConflicts(
143+
args: t.BranchesArgs & {
144+
author?: t.CommitAuthor;
145+
conflictResolveType: string;
146+
},
147+
): Promise<boolean>;
148+
142149
resolveRefs(
143150
args: t.RefsArgs & { type?: CommitDiffType },
144151
): Promise<{ fromCommitId: string; toCommitId: string }>;

graphql-server/src/queryFactory/mysql/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ export class MySQLQueryFactory
298298
throw notDoltError("merge branches");
299299
}
300300

301+
async callMergeWithResolveConflicts(_: t.BranchesArgs): Promise<boolean> {
302+
throw notDoltError("merge branches with conflicts");
303+
}
304+
301305
async resolveRefs(
302306
args: t.RefsArgs,
303307
): Promise<{ fromCommitId: string; toCommitId: string }> {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import Link from "@components/links/Link";
2+
import { Button, ExternalLink, Modal, Radio } from "@dolthub/react-components";
3+
import { ConflictResolveType } from "@gen/graphql-types";
4+
import { ModalProps } from "@lib/modalProps";
5+
import { PullDiffParams } from "@lib/params";
6+
import { pullConflicts } from "@lib/urls";
7+
import { useState } from "react";
8+
import css from "./index.module.css";
9+
10+
type Props = ModalProps & {
11+
onClickWithResolve: (resolveType: ConflictResolveType) => Promise<void>;
12+
params: PullDiffParams;
13+
};
14+
15+
export default function ResolveModal(props: Props) {
16+
const [resType, setResType] = useState<ConflictResolveType>(
17+
ConflictResolveType.Ours,
18+
);
19+
20+
return (
21+
<Modal
22+
title="Resolve Conflicts and Merge"
23+
isOpen={props.isOpen}
24+
onRequestClose={() => props.setIsOpen(false)}
25+
className={css.modal}
26+
button={
27+
<Button onClick={async () => await props.onClickWithResolve(resType)}>
28+
Choose {resType.toLowerCase()} and merge
29+
</Button>
30+
}
31+
>
32+
<div>
33+
<p>
34+
To merge this pull request, choose a conflict resolution strategy:
35+
</p>
36+
<Radio
37+
label="Use ours"
38+
name="ours"
39+
checked={resType === ConflictResolveType.Ours}
40+
onChange={() => setResType(ConflictResolveType.Ours)}
41+
/>
42+
<Radio
43+
label="Use theirs"
44+
name="theirs"
45+
checked={resType === ConflictResolveType.Theirs}
46+
onChange={() => setResType(ConflictResolveType.Theirs)}
47+
/>
48+
<p>
49+
You can view the table conflicts before merging{" "}
50+
<Link {...pullConflicts(props.params)}>here</Link>.
51+
</p>
52+
<p>
53+
If you&apos;d like more granular conflict resolution, see{" "}
54+
<ExternalLink href="https://www.dolthub.com/blog/2021-03-15-programmatic-merge-and-resolve/">
55+
this guide
56+
</ExternalLink>
57+
.
58+
</p>
59+
</div>
60+
</Modal>
61+
);
62+
}

0 commit comments

Comments
 (0)